Как применить функцию карты Seq? - PullRequest
3 голосов
/ 05 июня 2010

Я недавно играл с F #. Мне было интересно, вместо того чтобы использовать цикл for для генерации последовательности элементов, которые умножаются на все остальные элементы в списке, как я могу использовать функцию карты Seq или что-то подобное для генерации чего-то подобного ниже.

Так, например, У меня есть список [1..10] Я хотел бы применить забаву, которая генерирует результат, например,

[(1*1); (1*2);(1*3); (1*4); (1*5)......(2*1);(2*2);(2*3).....(3*1);(3*2)...]

Как мне этого добиться?

Большое спасибо за помощь.

Ответы [ 4 ]

6 голосов
/ 05 июня 2010
let list = [1..10]

list |> List.map (fun v1 -> List.map (fun v2 -> (v1*v2)) list) |> List.collect id

List.collect в конце сглаживает список списков. Он работает так же с Seq вместо List, если вы хотите ленивую последовательность.

Или, используя collect в качестве основного итератора, как предложил cfern и навязчиво исключил анонимные функции:

let flip f x y = f y x

let list = [1..10]

list |> List.collect ((*) >> ((flip List.map) list))
5 голосов
/ 05 июня 2010

Понимание списка было бы самым простым способом сделать это:

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)]

Я бы не использовал метод расширения в F # сам, это немного C # 'иш. Но это в основном потому, что я не чувствую, что в F # нужен свободный синтаксис, потому что я обычно объединяю свои функции в цепочку с операторами |

Мой подход заключается в расширении модуля List перекрестной функцией, а не самим типом:

module List =
    let cross f L1 L2 = 
        [for x in L1 do
            for y in L2 -> f x y]

Если вы сделаете это, вы можете использовать метод cross, как и любой другой метод List:

> 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]
3 голосов
/ 05 июня 2010

Или мы можем реализовать общую функцию кросс-произведения:

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
2 голосов
/ 06 июня 2010

Чтобы реализовать то же самое без циклов for, вы можете использовать решение, используя функции высшего порядка , опубликованные Mau, или вы можете написать то же самое явно, используя рекурсию:

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))) 

Кстати, в моей книге имеется бесплатная глава , в которой обсуждается, как работают выражения последовательности и List.collect функции. Стоит отметить, что for в выражениях последовательности напрямую соответствует List.collect, поэтому вы можете написать код, просто используя эту функцию более высокого порядка:

let cross xs ys =
  xs |> List.collect (fun v1 -> 
    ys |> List.collect (fun v2 -> [(v1, v2)] )) 

Однако, см. Свободную главу для получения дополнительной информации: -).

...