Перекрестное произведение из двух списков
Разнообразные функции расширения для модуля List.
(Я потратил довольно много времени на разработку "mapfold" - который накладывает такой накопитель как сгиб, но использует его как параметр для создания новых значений, таких как map, а затем обнаружил, что это то, что делает List.scan_left
)
Для генерации тестовых данных мне нужно было сделать перекрестное произведение из двух списков, вот что я придумал:
///Perform cross product of two lists, return tuple
let crossproduct l1 l2 =
let product lst v2 = List.map (fun v1 -> (v1, v2)) lst
List.map_concat (product l1) l2
Это хорошо, или есть ли еще лучший способ сделать это?
Тот же вопрос для этого:
///Perform cross product of three lists, return tuple
let crossproduct3 l1 l2 l3 =
let tuplelist = crossproduct l1 l2 //not sure this is the best way...
let product3 lst2 v3 = List.map (fun (v1, v2) -> (v1, v2, v3)) lst2
List.map_concat (product3 tuplelist) l3
Ответы
Ответ 1
другой вариант - использовать выражения последовательности F # и написать что-то вроде этого:
let crossproduct l1 l2 =
seq { for el1 in l1 do
for el2 in l2 do
yield el1, el2 };;
(на самом деле это почти то же самое, что и вы написали, потому что "for.. in.. do" в выражении последовательности можно рассматривать как map_concat). Это работает с (ленивыми) последовательностями, но если вы хотите работать со списками, вы просто переносите код внутри [...], а не внутри seq {...}.
Ответ 2
Просто натолкнулся на довольно элегантное решение, используя выражения вычислений:
type Product () =
member this.Bind (l,f) = List.collect f l
member this.Return n = [n]
let enumeratedPizzas =
Product() {
let! x = ["New York";"Chicago"]
let! y = ["Pepperoni";"Sausage"]
let! z = ["Cheese";"Double Cheese"]
return x,y,z
}
Techneilogy, скопированный из fssnip.net, следуйте ссылка, чтобы увидеть прокомментированный код.
Ответ 3
Функция кросспроизведения выглядит хорошо (вы, вероятно, заметили отсутствующие ключевые слова). Мне нравится эта версия crossproduct3 лучше, но что только я:
let crossproduct3 l1 l2 l3 =
List.map_concat
(fun z ->
(List.map_concat (fun y -> List.map (fun x -> (x, y, z)) l3) l2)) l1;;
Ваша функция имеет эквивалентную алгоритмическую сложность.
Наконец, при использовании кросспроизведения в явно пустом списке вы можете нажать на ограничение значения (грубо говоря, ограничение, гарантирующее компилятор только отображает полиморфные типы для синтаксического значения), что особенно строго в F #. Решение состоит в том, чтобы аннотировать вызовы, которые используют пустой список, следующим образом (если вы хотите, чтобы второй список состоял из целых чисел):
(crossproduct [3; 4] [] : (int * int) list)
Ответ 4
Я использовал Benjol ответ на некоторое время, пока не обнаружил, что вы можете в значительной степени сделать то же самое с Linq
. Решение Benjol, вероятно, все еще является самым изящным, но если кто-то хочет решение, которое вы можете использовать с C#
, также здесь вы идете:
query {
for i in [1, 2] do
for j in [3, 4] do
select (i, j)
}
Это почти то же самое, что и решение Томаса Петричека, за исключением того, что решение не должно быть вложенными петлями for
, поэтому оно немного проще.
Ответ 5
Мне недавно нужно было что-то подобное - мне пришлось закрепить список последовательностей в последовательности списков - так [(a1,a2,a3,...);(b1,b2,b3,...);(c1,c2,c3,...)] -> ([a1;b1;c1], [a2;b2;c3], ...)
Следующий код сделает следующее:
let rec listZip (sl : 'a seq list) =
seq {
match sl with
| [] -> yield []
| hd::tl ->
for h in hd do
for t in listZip tl do
yield (h::t)
}