2010-06-05 4 views
3

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

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

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

Как я могу добиться этого?.

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

ответ

6
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 предложил и obsessivley устранение скрытых функций:

let flip f x y = f y x 

let list = [1..10] 

list |> List.collect ((*) >> ((flip List.map) list)) 
+0

Спасибо @Mau отлично работает, операция «собрать» великолепна, наткнулся на первый раз после всех сообщений по моему вопросу. Большое спасибо – netmatrix01

5

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

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# 'ish. Но это в основном потому, что я не чувствую, что в F # необходим свободный синтаксис, потому что я обычно связываю свои функции вместе с операторами pipe (|>).

Мой подход был бы расширить модуль списка с крестом функции, а не сам тип:

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

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

> 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] 
+0

Thaks @cfern, мне нравится способ понимания списка. Еще одна вещь, которую я задавался вопросом, может ли вы на самом деле составить представление списка, которое вы показали как метод расширения, как у нас на C#. Если да, то как я могу сделать это методом расширения?. Хотелось бы узнать. Спасибо – netmatrix01

+0

@ netmatrix01: Я добавил дополнительный пояснительный метод для моего сообщения. Это ты имел в виду? – cfern

+0

Большое спасибо @cfern, ваше решение чрезвычайно элегантно. Я также вижу, что вы рекомендуете не использовать метод расширения, а расширять модуль List с помощью перекрестной функции. Его отличный подход. Еще раз спасибо. – netmatrix01

3

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

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 
+1

Или вы можете добавить параметр функции для пересечения, поэтому результат будет крест [1..10] [1..10] (*). Вопрос заключался в том, чтобы спрашивать, как это сделать без петель. – Mau

+0

Спасибо @Yin за ваше решение. Он отлично работает, и, насколько я понимаю, созданный список будет ленивым списком, который всегда будет хорошей идеей. Большое спасибо – netmatrix01

2

Чтобы реализовать то же самое без 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 

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

В качестве побочной ноты, первая редакцию Мау можно сделать немного лучше, потому что вы можете присоединиться вызовом List.map с вызовом List.collect id, как это (вы можете передать вложенную обработку лямбды непосредственно в качестве параметра collect). cross функция будет выглядеть следующим образом (Конечно, вы можете редактировать этот принять параметр, чтобы применить к двум номерам вместо создания кортежа):

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

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

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

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

+0

Спасибо @ Томас, блестящий ответ. Я особенно ценю ваши усилия в отношении функции рекурсии.Я согласен с вашим комментарием в качестве новичка в FP, мне было бы полезно узнать методы рекурсии. Я действительно получил вашу книгу около 4 -5 месяцев назад, просто из-за трудовых обязательств удалось пройти мимо страницы 30-35. Но очень скоро я собираюсь читать и экспериментировать со всеми примерами в вашей обложке книги. Большое спасибо. – netmatrix01

+0

Я просто пробовал вашу технику рекурсии, но, к сожалению, я получаю исключение Stackoverflow. Я также пытался его отладить, но он взрывается, как только он пытается ввести блок соответствия. Поскольку я новичок в этой технике, я не могу решить проблему. Было бы полезно, если бы вы могли сказать, что может быть проблемой. На аналогичной ноте было интересно, что является лучшим методом для отладки функции рекурсии, поскольку я всегда находил их сложными. Большое спасибо. – netmatrix01

+0

@ netmatrix01: Для каких вкладов вы получаете исключение? К сожалению, отладка рекурсии действительно довольно сложная - в Visual Studio вы можете просматривать трассировку стека и перемещаться по ней, чтобы увидеть рекурсивные вызовы, которые были сделаны. Это часто очень полезно. –

Смежные вопросы