Вопрос

Я недавно играл с 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)] )) 

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top