是否存在一个现有模式来为两个列表中的每个项目组合生成一个函数应用程序列表?

时间:2010-02-12 16:46:33

标签: f# functional-programming terminology

我刚刚进入函数式编程,我正在“尝试一些非平凡的例子,并问别人我是否做错了”阶段。我跟随Don Syme的F# Tutorial并决定在第二部分结束时对二十一点练习进行一次尝试:为了简单起见,他建议将Ace视为11,但我决定忽略这一建议。

我正在处理它的方法是给每张卡片等级一个可能的值列表,并递归地建立一个可能的手值列表:

let cardValues (Card(rank, _)) =
    match rank with
    | Ace                 -> [1; 11]
    | King | Queen | Jack -> [10]
    | Value(value)        -> [value]

let rec handValues = function
    | [] -> [0]
    | card::cards ->
        [
            for handValue in handValues cards do
                for cardValue in cardValues card do
                    yield handValue + cardValue
        ]

handValues函数在结构上与折叠非常相似,我无法感觉到已经有一些高阶函数可以用来实现这一点。有没有我缺少的东西,或者这是非常正确的方向?

3 个答案:

答案 0 :(得分:4)

值得一提的是这个

    [ 
        for handValue in handValues cards do 
            for cardValue in cardValues card do 
                yield handValue + cardValue 
    ] 

是一个monadic绑定;一个人可以创建一个'list'monad,然后使用计算表达式将其写为

listMonad {
    let! handVal = handValues cards
    let! cardVal = cardValues card
    return hardVal + cardVal
}

答案 1 :(得分:2)

你做事的方式非常好。可以在列表上表达任何递归函数作为折叠,但我不认为你通过这样做获得任何东西。还没有内置函数可以完全满足您的需求,但您可以构建更通用的函数并在此基础上构建您的特定计算。这是一种这样的方法:

let rec allChoices = function
| [] -> [[]]
| l::ls ->
    [for x in l do
     for xs in allChoices ls do
       yield x::xs]

let values hand = 
  hand |>
  List.map cardValues |>
  allChoices |> 
  List.map (List.sum)

allChoices函数获取列表列表并返回每个可能的列表,其中包含每个列表中的单个元素(例如allChoices [[1];[2;3];[4;5]] = [[1;2;4];[1;2;5];[1;3;4];[1;3;5]])。我们使用此函数来获取手中卡片的所有可能值列表,然后对每个这样的列表求和。

您可能还有其他几种方法可以查看可能暗示其他变化的问题。

答案 2 :(得分:1)

我认为您的解决方案已经很好了。

折叠不适用于您的情况。我们可以折叠一个数字列表,我们也可以折叠两个数字列表。但在你的情况下,它不仅仅是两个数字列表。

考虑一个极端情况,即你的列表包含长度为n的所有A,然后有2 ^ n个可能的值。要枚举所有可能性,您需要dfs搜索或bfs搜索。您的代码实际上等同于bfs搜索(因此它需要更多内存),尽管它以递归方式写入。