Вопрос
Я недавно играл с F#. Мне было интересно вместо того, чтобы использовать цикл для сгенерирования последовательности на элемент, который умножается на любой другой элемент в списке, как я могу использовать функцию карты SEQ или что -то похожее, чтобы генерировать что -то вроде ниже.
Так что, например, у меня есть список [1..10], я хотел бы применить веселье, которое приносит результат что -то вроде
[(1*1); (1*2);(1*3); (1*4); (1*5)......(2*1);(2*2);(2*3).....(3*1);(3*2)...]
Как я могу этого добиться?
Большое спасибо за все, что вы помогаете.
Решение
let list = [1..10]
list |> List.map (fun v1 -> List.map (fun v2 -> (v1*v2)) list) |> List.collect id
Список. КОЛЛЕКТИВАТЬ В конце ПЕРЕСЬ ПЕРЕСМО СПИСОК Список. Это работает так же с SEQ вместо списка, если вам нужна ленивая последовательность.
Или, используя collect
Как основной итератор, как предложил Cfern и Obsessivley исключает анонимные функции:
let flip f x y = f y x
let list = [1..10]
list |> List.collect ((*) >> ((flip List.map) list))
Другие советы
Понимание списка будет самым простым способом сделать это:
let allpairs L =
[for x in L do
for y in L -> (x*y)]
Или, не используя петли:
let pairs2 L = L |> List.collect (fun x -> L |> List.map (fun y -> (x*y)))
Измените в ответ на комментарий:Вы можете добавить метод расширения самокрипения в список, подобный этому:
type Microsoft.FSharp.Collections.List<'a> with
member L.cross f =
[for x in L do
for y in L -> f x y]
Пример:
> [1;2;3].cross (fun x y -> (x,y));;
val it : (int * int) list =
[(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]
Я бы не стал использовать метод расширения в себе, это кажется немного C# 'Ish. Но это в основном потому, что я не чувствую, что в F# необходим беглый синтаксис, потому что я обычно складываю свои функции вместе с операторами трубы (|>).
Мой подход - расширить модуль списка с помощью кросс -функции, а не самого типа:
module List =
let cross f L1 L2 =
[for x in L1 do
for y in L2 -> f x y]
Если вы сделаете это, вы можете использовать метод Cross, как и любой другой метод списка:
> List.cross (fun x y -> (x,y)) [1;2;3] [1;2;3];;
val it : (int * int) list =
[(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]
> List.cross (*) [1;2;3] [1;2;3];;
val it : int list = [1; 2; 3; 2; 4; 6; 3; 6; 9]
Или мы можем реализовать общую функцию кросс -продукта:
let cross l1 l2 =
seq { for el1 in l1 do
for el2 in l2 do
yield el1, el2 };;
и используйте эту функцию, чтобы выполнить работу:
cross [1..10] [1..10] |> Seq.map (fun (a,b) -> a*b) |> Seq.toList
Реализовать одно и то же без for
петли, вы можете использовать решение, используя функции высшего порядка Автор: Мау, или вы можете написать то же самое явно, используя рекурсию:
let cross xs ys =
let rec crossAux ol2 l1 l2 =
match l1, l2 with
// All elements from the second list were processed
| x::xs, [] -> crossAux ol2 xs ol2
// Report first elements and continue looping after
// removing first element from the second list
| x::xs, y::ys -> (x, y)::(crossAux ol2 l1 ys)
// First list is empty - we're done
| [], _ -> []
crossAux ys xs ys
Это может быть полезно, если вы изучаете функциональное программирование и рекурсию, однако решение с использованием выражений последовательностей гораздо более полезно.
Как боковая нота, первая версия Mau может быть немного приятнее, потому что вы можете присоединиться к вызову к List.map
с призывом к List.collect id
Как это (вы можете передать вложенную обработку лямбда непосредственно в качестве параметра в collect
) А cross
Функция выглядела бы так (конечно, вы можете изменить это, чтобы применить параметр, чтобы применить два числа вместо создания кортежа):
let cross xs ys =
xs |> List.collect (fun v1 ->
ys |> List.map (fun v2 -> (v1, v2)))
Между прочим, есть Бесплатная глава Из моей книги Avaialable, в которой обсуждается, как выражения последовательности и List.collect
функции работают. Стоит отметить, что for
в выражения последовательности непосредственно соответствует List.collect
, поэтому вы можете написать код, просто используя эту функцию более высокого порядка:
let cross xs ys =
xs |> List.collect (fun v1 ->
ys |> List.collect (fun v2 -> [(v1, v2)] ))
Тем не менее, см. Бесплатную главу для получения дополнительной информации :-).