Умножение списков через сворачивание - PullRequest
0 голосов
/ 17 ноября 2018

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

eg) let prodList [1; 2; 3] [4; 5; 6] ;; 
==> (through folding) ==> [1*4; 2*5; 3*6]
==> result = [4; 10; 18]

Я чувствую, что мне нужно использовать List.combine, так как он поместит значения, которые нужно умножить, в кортежи.После этого я не могу понять, как разделить кортеж таким образом, чтобы я мог умножить значения.Вот что у меня есть:

let prodLists l1 l2 =
  let f a x =  (List.hd(x)) :: a in
  let base = [] in
  let args = List.rev (List.combine l1 l2) in
  List.fold_left f base args

Я на правильном пути?

Ответы [ 3 ]

0 голосов
/ 19 ноября 2018

Во-первых, позвольте мне заметить, что использование Fold для реализации этой операции выглядит несколько вынужденным, поскольку вы должны проходить по обоим спискам одновременно.Однако Fold объединяет элементы единого списка.Тем не менее, здесь есть реализация.

let e [] = []

let f x hxs (y::ys) = (x*y) :: hxs ys 

let prodList xs ys = List.fold_right f xs e ys

Выглядит немного сложно, поэтому позвольте мне объяснить.

Универсальное свойство сгиба вправо

Сначала вы должны знать следующее свойство fold_right.

h xs = fold_right f xs e 

тогда и только тогда, когда

h [] = e
h (x::xs) = f x (h xs)

Это означает, что если мы запишем умножение списков в рекурсивной форме ниже, то мы можем использовать e и fнаписать это с помощью сгиба, как указано выше.Обратите внимание, что мы работаем с двумя списками, поэтому h принимает два аргумента.

Базовый случай - пустые списки

Умножение двух пустых списков возвращает пустой список.

h [] [] = []

Как записать это в форме выше?Просто абстрагируйтесь по второму аргументу.

h [] = fun [] -> []

Итак,

e = fun [] -> []`

Или, что эквивалентно,

e [] = []

Рекурсивный регистр - непустые списки

h (x::xs) (y::ys) = x*y :: h xs ys

Или, используя только один аргумент,

h (x::xs) = fun -> (y::ys) -> x*y :: h xs ys

Теперь нам нужно переписать это выражение в виде h (x::xs) = f x (h xs).Это может показаться сложным, но нам просто нужно абстрагироваться от x и h xs.

h (x::xs) = (fun x hxs -> fun (y::ys) -> x*y :: hxs ys) x (h xs)

, поэтому мы имеем, что f определяется как

f = fun x hxs -> fun (y::ys) -> x*y :: hxs ys

или, что эквивалентно,

f x hxs (y::ys) = x*y :: hxs ys

Решение в виде сгиба вправо

Определив как e, так и f, мы просто подключаем затем в сгиб согласно первому уравнению свойства выше.И мы получим

h xs = List.fold_right f xs e

или эквивалентно

h xs ys = List.fold_right f xs e ys

Понимание реализации

Обратите внимание, что тип List.fold_right f xs e равен int list -> int list, таким образом, фолд строит функцию по спискам, которая при некотором ys умножит его на заданный параметр xs.

Для пустого xs вы ожидаете пустое ys и вернете пустой результат так,

e [] = fun [] -> []

Что касается рекурсивного случая, то функция f в fold_right должен реализовать решение для x::xs из решения для xs.Поэтому f принимает x типа int и функцию hxs типа int list -> int list, которая реализует умножение для хвоста, и она должна реализовывать умножение для x::xs.

f x hxs = fun (y::ys) -> x*y :: hxs ys

Итак, f создает функцию, которая умножает x на y, а затем применяет к ys уже построенному hxs, который умножает xs на список.

0 голосов
/ 19 ноября 2018

У вас в основном правильная идея; вам нужно будет combine (zip на других языках) два списка, а затем map для каждого кортежа:

let prod_lists l1 l2 =
  List.combine l1 l2
  |> List.map (fun (a, b) -> a * b)

Ключ в том, что вы можете сопоставить шаблон на этом кортеже, используя (a, b).

Вы также можете fold в комбинированном списке, а затем rev результат, если вы не хотите использовать map.

0 голосов
/ 18 ноября 2018

Вы можете использовать fold_left2, который сворачивает два списка одинаковой длины.Документация может дать вам более подробную информацию (https://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html):

val fold_left2 : ('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'a

List.fold_left2 f a [b1; ...; bn] [c1; ...; cn] равно f (... (f (f a b1 c1) b2 c2) ...) bn cn. Поднимите Invalid_argument, если два списка имеют разную длину.

Другойспособ сложить выход комбайна, как вы предложили, я бы порекомендовал вам попробовать его самостоятельно, прежде чем смотреть на решение ниже.

Решение:

let prod_lists l s =
  List.rev (List.fold_left2 (fun acc a b -> (a * b) :: acc) [] l s);;

let prod_lists' l s =
  List.fold_left (fun acc (a, b) -> (a * b) :: acc) [] (List.rev (List.combine l s));;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...