Список F # SelectMany
Это довольно простой вопрос, но я не нашел ответа:
Есть ли какая-либо операция Seq/List в F # для соответствия LINQ SelectMany?
- Я знаю, что могу использовать System.Linq в F #, если я
хочу.
- Я знаю, что могу сделать рекурсивный метод
и использовать выражения вычисления F #
(и сделать еще более мощные вещи).
Но если я попытаюсь доказать, что операции F # List более мощные, чем LINQ...
- .Where = List.filter
- .Select = List.map
- .Aggregate = List.fold
- ...
В С# SelectMany синтаксис использования довольно прост:
var flattenedList = from i in items1
from j in items2
select ...
Есть ли легкое прямое совпадение, List.flatten, List.bind или что-то в этом роде?
SelectMany имеет пару подписей, но наиболее сложным является:
IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TCollection>> collectionSelector,
Func<TSource, TCollection, TResult> resultSelector
);
В терминах F # это будет:
('a -> 'b list) -> ('a -> 'b -> 'c) -> 'a list -> 'c list
Ответы
Ответ 1
collect
является эквивалентом F # SelectMany, однако он не обеспечивает всех перегрузок. Здесь, как сделать тот, на который вы ссылаетесь.
let selectMany (ab:'a -> 'b seq) (abc:'a -> 'b -> 'c) input =
input |> Seq.collect (fun a -> ab a |> Seq.map (fun b -> abc a b))
// gives
// val selectMany : ('a -> seq<'b>) -> ('a -> 'b -> 'c) -> seq<'a> -> seq<'c>
Я считаю, что F # не предоставляет всех перегрузок SelectMany, потому что они добавили бы шум в библиотеку. Здесь все четыре перегрузки до SelectMany в Microsoft Naming.
let selectMany (source : 'TSource seq) (selector : 'TSource -> 'TResult seq) =
source |> Seq.collect selector
let selectMany (source : 'TSource seq) (selector : 'TSource -> int -> 'TResult seq) =
source |> Seq.mapi (fun n s -> selector s n) |> Seq.concat
let selectMany (source : 'TSource)
(collectionSelector : 'TSource -> 'TCollection seq)
(resultSelector : 'TSource -> 'TCollection -> 'TResult) =
source
|> Seq.collect (fun sourceItem ->
collectionSelector sourceItem
|> Seq.map (fun collection -> resultSelector sourceItem collection))
let selectMany (source : 'TSource)
(collectionSelector : 'TSource -> int -> 'TCollection seq)
(resultSelector : 'TSource -> 'TCollection -> 'TResult) =
source
|> Seq.mapi (fun n sourceItem ->
collectionSelector sourceItem n
|> Seq.map (fun collection -> resultSelector sourceItem collection))
|> Seq.concat
"Операции с F # List более мощные, чем LINQ..." В то время как операции seq/list велики, реальная "мощность F #" происходит от Function Composition и Currying.
// function composition
let collect selector = Seq.map selector >> Seq.concat
Ответ 2
Вы можете использовать List.collect или Seq.Collect:
let items1 = [1; 2; 3]
let items2 = [4; 5; 6]
let flat = items1 |> List.collect (fun i1 -> items2 |> List.map (fun i2 -> [i1, i2]))
Это будет примерно эквивалентно следующему С# -коду:
var flat = from i1 in items1
from i2 in items2
select new { i1, i2 };
Ответ 3
Другие сообщения показывают, как сопоставить linq с
Начиная с этого linq:
var flattenedList = from i in items1
from j in items2
select ...
var flattenedList2 = items1.SelectMany(i => items2.Map(j => ...))
Эквивалент F # равен:
let flattenedList = seq {
for a in items1 do
for b in items2 do
yield ... }
let flattenedList2 = items1 |> Seq.collect (fun i -> items2 |> Seq.map (fun j -> ...))
Два бита кода примерно эквивалентны по выразительности и сложности.
С учетом сказанного, позвольте указать конкретный комментарий в вашем сообщении:
Но если я попытаюсь доказать, что список F # операции более мощные, чем LINQ...
Операции в модулях Seq/List примерно эквивалентны расширениям Enumerable/Linq.
Однако, я бы сказал, что функция killer для списков - это возможность сопоставить шаблон. Здесь глупый пример, который не легко конвертируется в linq:
let rec funky = function
| x::y::z::rest -> (z, y)::funky(z::x::rest)
| [y;z]-> [(z, y)]
| [z] -> [(z, z)]
| [] -> []
// funky [1..6]
// = (int * int) list = [(3, 2); (4, 1); (5, 3); (6, 4)]
Это было бы немного неудобно для переопределения на С#, но его мертвая простота записи F #.
Ответ 4
Seq.bind
- это то, что вы хотите. SelectMany
на самом деле просто монадическое связывание:).
Итак, вы бы сделали:
seq { for i in items1 do
for j in items2 do
yield .... };