Привет всем, я так давно не писал техническую статью, и сегодняшняя будет посвящена одной проблеме из пяти серий решений, в которых мы будем реализовывать функцию 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 в каждом языке.
Спасибо, что дочитали до сюда, и если есть проблема, которую вы хотели бы, чтобы я реализовал в этой серии, не стесняйтесь, дайте мне комментарий ниже.