在列表理解中使用许多相同生成器的惯用方法

Per*_*ril 8 haskell list-comprehension idiomatic

在Statistics课程中,我的老师向我们展示了两个骰子的所有可能卷的概率模型,增加到4.记住Haskell列表推导非常棒,我决定将它带到下一步并编写此代码以查找所有可能增加10个骰子的4个骰子:

[(d1,d2,d3,d4) | d1 <- [1..6], d2 <- [1..6], d3 <- [1..6], d4 <- [1..6], (d1 + d2 + d3 + d4) == 10]

这按预期工作,给我输出

[(1,1,2,6),(1,1,3,5),(1,1,4,4),(1,1,5,3),(1,1,6,2) ,(1,2,1,6),(1,2,2,5),(1,2,3,4),(1,2,4,3),(1,2,5,2) ,(1,2,6,1),(1,3,1,5),(1,3,2,4),(1,3,3,3-),(1,3,4,2) ,(1,3,5,1),(1,4,1,4),(1,4,2,3),(1,4,3,2),(1,4,4,1) ,(1,5,1,3),(1,5,2,2),(1,5,3,1),(1,6,1,2),(1,6,2,1) ,(2,1,1,6),(2,1,2,5),(2,1,3,4),(2,1,4,3),(2,1,5,2) ,(2,1,6,1),(2,2,1,5),(2,2,2,4),(2,2,3,3),(2,2,4,2) ,(2,2,5,1),(2,3,1,4),(2,3,2,3),(2,3,3,2),(2,3,4,1) ,(2,4,1,3),(2,4,2,2),(2,4,3,1),(2,5,1,2),(2,5,2,1) ,(2,6,1,1),(3,1,1,5),(3,1,2,4),(3,1,3,3),(3,1,4,2) ,(3,1,5,1),(3,2,1,4),(3,2,2,3),(3,2,3,2),(3,2,4,1) ,(3,3,1,3),(3,3,2,2),(3,3,3,1),(3,4,1,2),(3,4,2,1) ,(3,5,1,1),(4,1,1,4),(4,1,2,3),(4,1,3,2),(4,1,4,1) ,(4,2,1,3),(4,2,2,2),(4,2,3,1),(4,3,1,2),(4,3,2,1) ,(4,4,1,1),(5,1,1,3),(5,1,2,2),(5,1,3,1),(5,2,1,2) ,(5,2,2,1),(5,3,1,1),(6,1,1,2),(6,1,2,1),(6,2,1,1) ]

这就是我的问题所在.Ruby是我背景的重要组成部分,因此我对DRY原则非常重视.有d1 <- [1..6], d2 <- [1..6], d3 <- [1..6], d4 <- [1..6]在我的代码似乎没有必要,我愿意相信有一个更好的方式来做到这一点.

据我所知,我当前的方法通过在幕后运行4个嵌套循环来实现 - 每个生成器一个.有没有办法让一个<- [1..6]生成器适用于所有变量,有效地创建4个嵌套循环?如果没有,是否有更少冗余或惯用的方式编写此代码以实现相同的结果?

注意:我对这门语言很陌生,所以如果这是显而易见的话我会道歉.如果您使用任何新手命令式/面向对象程序员不熟悉的单词/概念,请尝试为我解释它们.

rya*_*hza 12

如果你担心的是[1..6]重复(范围独立变化的能力),你可以使用:

let die = [1..6] in [ (d1,d2,d3,d4) | d1 <- die, d2 <- die
                                    , d3 <- die, d4 <- die
                                    , (d1 + d2 + d3 + d4) == 10 ]
Run Code Online (Sandbox Code Playgroud)

总的来说,要删除显式模具命名,虽然这不完全相同,因为它将是列表而不是元组:

let die = [1..6] in [dice | dice <- sequence (replicate 4 die), sum dice == 10]
Run Code Online (Sandbox Code Playgroud)

要恢复元组,您可以进行模式匹配,但如果输入表达式更改为模式匹配失败,则可能会导致难以跟踪的错误,只会排除元素:

let die = [1..6] in
  [ (d1,d2,d3,d4) | dice@[d1,d2,d3,d4] <- sequence (replicate 4 die)
                  , sum dice == 10 ]
Run Code Online (Sandbox Code Playgroud)


eps*_*lbe 9

如果你想坚持下去4-Tuples,也就是说(1,1,3,5),这不是很优雅,但如果你愿意使用列表,那么你可以非常优雅地管理

import Control.Monad
listSum10 = filter ((==10) . sum) $ replicateM 4 [1..6]
          = [dice | dice <- replicateM 4 [1..6], sum dice == 10]
Run Code Online (Sandbox Code Playgroud)

或使用do-notation

listSum10 = do x <- replicateM 4 [1..6]
               guard $ sum x == 10
               return x
Run Code Online (Sandbox Code Playgroud)