从两个数组创建元组的更实用的方法

时间:2018-03-09 09:40:07

标签: f# functional-programming

我创建了一个函数,它将所有整数从1转换为n,然后结合相同的序列来创建所有组合的元组序列。因此传递整数2会给你[(1,1);(1,2);(2,1);(2,2)]

let allTuplesUntil x =
    let primary = seq { 1 .. x }
    let secondary = seq { 1 .. x }
    [for x in primary do
     for y in secondary do
     yield (x,y)]

此实现有效,但它使用内部和外部for循环,类似于我在c#中的操作。

这可以通过更惯用的功能方式实现吗?一种更实用的方式通常是更理想的,还是因为其简洁明了而在功能语言中可以接受?

我对f#相对较新并且正在寻找一些反馈。

2 个答案:

答案 0 :(得分:4)

这些循环是所谓的计算表达式的一部分,这对于F#来说非常惯用。它只是看起来像熟悉的循环。我看不出你的代码是以这种方式编写的任何问题。如果您想要摆脱循环,可以将它们隐藏在函数中:

let cartesianProduct xs ys = 
     xs |> Seq.collect (fun x -> ys |> Seq.map (fun y -> x, y))

cartesianProduct [1;2;3] ['a';'b';'c']

val it : seq<int * char> = seq [(1, 'a'); (1, 'b'); (1, 'c'); (2, 'a'); ...]

答案 1 :(得分:1)

首先,仅仅因为for并不意味着它不起作用。在此示例中,您将遍历每个元素并生成一个新元素,该元素将转换为新的不可变列表的新元素。这样的功能也被命名为#34; List Comprehension&#34;和Haskell等语言的一部分。势在必行的是循环遍历列表并改变列表。

其次,请记住mapfoldfilter等其他函数也只是遍历每个元素,就像for表达式一样。它们的功能不如for循环。

第三,即使它是&#34;也不是100%功能&#34;。谁在乎?代码应易于阅读和理解。两个for循环的意图很容易理解。

第四,for表达式的等效函数通常是bind,或者在这种情况下是Seq.collect函数。你也可以写这段代码。

[for x in primary do
 for y in secondary do
 yield (x,y)]

像这样:

primary |> Seq.collect (fun x ->
secondary |> Seq.collect (fun y ->
    [x,y]
))

我更喜欢for循环以提高可读性!