Option <T>集合的功能转换

时间:2019-11-24 17:43:54

标签: c# scala haskell functional-programming monads

我有一个Option<T>实例的集合,想从IEnumerable<Option<T>>转换为Option<IEnumerable<T>>

如果 ALL 选项中有一个值,那么我想要一个Some<IEnumerable<T>>并带有收集的值,如果集合中 ANY 个项目是{{ 1}},然后我要一个None

这似乎是一个相当普通的功能转换,但是我不确定它是否存在于主流功能库中,或者被称为什么。似乎与None<IEnumerable<T>>类似,但不完全相同,因为我不想只滤除FlatMap的值。

我可以自己实现它,但想了解它是否已经作为功能构造存在。不用介意使用什么语言,C#,Scala,Haskell等。

6 个答案:

答案 0 :(得分:2)

这些语言结构最初不是用C#表示的。但是,为此目的有多个功能编程库。我的选择是Paul Louth的language-ext;到目前为止,我为C#找到的最丰富的FP库。

答案 1 :(得分:2)

功能编程概念有多个“语言族”。

在Haskell(显然是Scala)中,这称为connect.Method = "connect"; connect.Data = new Dictionary<string, object>(); connect.Data.Add("data", new Dictionary<string, object>() { { "somekey", "data" }, { "somekey2", "data" }, { "somekey3", "data" }, { "somekey4", "data" } } ); ,是称为Traversable的抽象或类型类的一部分; sequence是遍历的一种特殊情况。

存在其他语言家族,最著名的是ML。这就是术语sequence的来源(在Haskell中称为Option)。虽然我精通F#(这是一种ML方言),但我不知道遍历/顺序在这里已经建立了专门的术语。

答案 2 :(得分:1)

正如您说的那样,您不介意该语言,我在这里有一个使用F#的示例,该示例开箱即用Namespace.GetAddressEntryFromID

Option<T>

如果您不熟悉F#,let chooseAll source = let anyNone = source |> Seq.exists Option.isNone if anyNone then None else Some source let s = [Some 1; Some 2; None] let result1 = s |> chooseAll // None let s = [Some 4; Some 5] let result2 = s |> chooseAll // Some {4;5} Seq的类型别名。这里的IEnumerable<T>具有签名chooseAll,其翻译为C#友好语法为seq<'a option> -> seq<'a> option

答案 3 :(得分:0)

您可以滚动自己的扩展方法,该方法依赖于System.Linq命名空间中的可用原语:

public static Option<IEnumerable<T>> AllSome<T>(this IEnumerable<Option<T>> input)
            where T : class
            => (input.All(o => o.IsSome)) ?
                new Some<IEnumerable<T>>(input.Select(o => (o as Some<T>).Value)) :
                new None<IEnumerable<T>>() as Option<IEnumerable<T>>;

这是基于我为支持该示例而编写的Option类型的一次性实现的,可以根据您的Option风格随意调整:

public class Option<T>
    where T: class
{
    protected T value;
    public bool IsSome => value != default(T);
}

public class Some<T> : Option<T>
    where T: class
{
    public Some(T value) => base.value = value;
    public T Value => base.value;
}

public class None<T> : Option<T>
    where T: class
{

}

答案 4 :(得分:0)

我玩了一点,在这种情况下似乎可以使用f#fold和foldBack。以下是一些示例:

let l1 = [Some 1; Some 2; None]
let l2 = [Some 3; Some 4; Some 5]


let acc (ac: int list option) le = 
    match ac, le with
    | Some ac, Some le -> Some (le::ac)
    | _, _ -> None

//this will reverse the order of elements if all present
let r1 = l1 |> List.fold acc (Some [])
let r2 = l2 |> List.fold acc (Some [])

//the order of elements preserved
let acc2 le (ac: int list option) = acc ac le
let r3 = List.foldBack acc2 l1 (Some [])
let r4 = List.foldBack acc2 l2 (Some [])

//or...
let r3b = l1 |> List.foldBack acc2 <| (Some [])
let r4b = l2 |> List.foldBack acc2 <| (Some [])

答案 5 :(得分:0)

此处是语言扩展作者,您只需调用.Sequence()即可反转内部和外部单子信号。

IEnumerable<Option<int>> items = ...;

Option<IEnumerable<int>> result = items.Sequence();

如果序列中的任何一项为None,则返回None,否则为Some,它具有您要查找的返回.Traverse(...)的确切行为。

您还可以随时使用ROW_NUMBER()映射结果。