Привет всем, я так давно не писал техническую статью, и сегодняшняя будет посвящена одной проблеме из пяти серий решений, в которых мы будем реализовывать функцию zip.

Функция zip — это функция, которая упаковывает два списка и возвращает третий список кортежей из двух элементов, а функция zip хорошо известна программистам на Haskell благодаря тому, что она присутствовала в прелюдии Haskell.

На практике функция zip в Haskell выглядит так:

zip [1, 2, 3] [4, 5, 6]
-- outputs [(1,4),(2,5),(3,6)]
zip [1, 2, 3] [4, 5]
-- outputs [(1,4),(2,5)]
zip [1, 2] [4, 5, 6]
-- outputs [(1,4),(2,5)]

Недавно я программировал на языке, который по умолчанию не поддерживал функцию zip, поэтому мне пришлось реализовать ее самостоятельно, что и породило тему сегодняшней статьи.

После надлежащего введения давайте реализуем наши решения.

Хаскелл

Как я уже упоминал, в Haskell есть функция zip, но я считаю, что для этой статьи будет очень поучительно, если мы начнем с реализации функции zip с нуля в Haskell, просто в учебных целях.

myzip [] _ = []
myzip _ [] = []
myzip (x:xs) (y:ys) = (x, y):myzip xs ys

Всего в трех строках кода, используя сопоставление с образцом, мы реализуем нашу функцию zip, которая в основном состоит из проверки:

  • если первый список пуст, мы не можем соединить его в кортеж, поэтому возвращаем пустой список
  • если второй список пуст, мы все равно не можем иметь пару в кортеже и возвращать пустой список
  • если в обоих списках все еще есть элементы, мы получаем первый элемент x первого списка и первый элемент y второго списка, добавляем их в кортеж и добавляем их в массив с помощью символа ':' и рекурсивно вызываем 'myzip' функция с остальными обоими списками

Эликсир

Продолжайте работать с функциональными языками, вторым решением будет Elixir:

defmodule Zip do
  def zip(_, []), do: []
  def zip([], _), do: []
  def zip([x | xs], [y | ys]), do: [{x, y} | zip(xs, ys)]
end

Решение очень похоже на решение Haskell, с той разницей, что используется символ «|» для подключения элемента заголовка к списку, который будет построен рекурсивно.

JavaScript

Теперь настала очередь строительного блока сети, давайте посмотрим на функцию zip JavaScript:

const zip = (ls1, ls2) => {
   const zips = []
   for (let i = 0; i < ls1.length; i++) {
      if (!ls2[i])
          break
      zips.push([ls1[i], ls2[i]])
   }
   return zips
}

JavaScript не очень хорошо поддерживает рекурсию и имеет проблемы с хвостовым вызовом, поэтому более распространенным подходом к функции zip является использование цикла, который проходит по первому списку и в теле цикла проверяет, присутствует ли элемент в индексе ' i' в массиве 'ls2', если да, поместите еще одну пару элементов в массив zips, если нет, разорвите цикл и верните zips

Также обратите внимание, что в JavaScript нет типа данных tuple, поэтому вместо него был выбран массив.

питон

Подобно JavaScript, python является интерпретируемым языком, но решение немного отличается:

def zip(ls1, ls2):
  zips = []
  max_index = min(len(ls1), len(ls2))
  for i in range(0, max_index):
    zips.append((ls1[i], ls2[i]))
  return zips

В решении на Python я добавляю кортеж в массив zips, но для работы решения мне нужно было получить длину более короткого массива, создать диапазон, начинающийся с нуля и заканчивающийся на max_index-1, и после запуска цикла Я просто возвращаю пары кортежей

Вланг

Последнее решение — это решение Vlang, V — это типизированный язык со вкусом GO и неизменностью, и для этого у решения есть собственный способ реализации V:

import math 
pub fn zip<T>(ls1 []T, ls2 []T) [][]T { 
    total := math.min(ls1.len, ls2.len) 
    return [][]T{len: total, init: [ls1[it], ls2[it]]}  
}

Во-первых, нам нужно импортировать математический модуль, чтобы иметь возможность использовать функцию min для получения общего количества пар, которые мы получим в массиве.

В объявлении функций и параметров нам пришлось использовать «T», чтобы сообщить, что функция будет универсальной для нескольких типов.

После получения всех элементов мы возвращаем массив '[]T', заполненный парами '[ ls1[it], ls2[it]]', также обратите внимание, что 'it' работает как индекс позиций массива, которые начинаются в ноль и заканчивается в сумме-1.

Улучшение решения редактора

После просмотра этого решения 21 апреля 2023 года на Vlang было разработано новое решение для работы с несколькими типами, которое сделало его более похожим на решение Haskell путем создания типа кортежа:

struct Tuple<T, V> {
   x T
   y V
}

fn zip<T, V>(ls1 []T, ls2 []V) []Tuple<T, V> { 
    total := math.min(ls1.len, ls2.len)
    return []Tuple<T, V>{len: total, init: Tuple<T, V>{x: ls1[it], y: ls2[it]}}
}

Создав структуру кортежа, мы можем определить элементы x и y с переменными разных типов и иметь функцию zip, способную работать с разными типами в списках, например:

zip([1, 2, 3], [true, false, true])

генерирует вывод, например:

[Tuple[int, bool]{
    x: 1
    y: true
}

И для доступа к данным:

zip([1, 2, 3], [true, false, true])[0].x //1
zip([1, 2, 3], [true, false, true])[1].y // false

И это демонстрация наших решений. Мне очень понравилось реализовывать задачу функции zip на нескольких языках, потому что каждое решение в этой статье было уникальным, и было очень интересно узнать, как реализовать функцию zip в каждом языке.

Спасибо, что дочитали до сюда, и если есть проблема, которую вы хотели бы, чтобы я реализовал в этой серии, не стесняйтесь, дайте мне комментарий ниже.