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

发布于 2024-08-22 02:48:24 字数 846 浏览 2 评论 0原文

我刚刚进入函数式编程,并且正处于“尝试一些重要的示例并询问其他人我是否做错了”阶段。我正在关注 Don Syme 的 F# 教程 并决定在第二部分末尾尝试一下二十一点练习:为了简单起见,他建议将 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 函数在结构上与折叠非常相似,因此我无法动摇我已经可以使用一些高阶函数来完成此任务的感觉。我是否缺少某些东西或者这几乎是正确的方向?

I'm just getting into functional programming and i'm in the "try out some non-trivial examples and ask others if I'm doing it wrong" phase. I'm following Don Syme's F# Tutorial and have decided to take a stab at the blackjack exercise at the end of Part II with a twist: he suggests treating Ace as 11 for simplicity's sake, but I decided to ignore that recommendation.

The way I'm handling it is by giving each card rank a list of possible values and building up a list of possible hand values recursively thus:

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
        ]

The handValues function is so similar in structure to a fold that I can't shake the feeling there's already some high order function I can use to accomplish this. Is there something I'm missing or is this pretty much the right direction?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

つ低調成傷 2024-08-29 02:48:24

值得一提的是,这

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

是一个单子绑定;人们可以编写一个“列表”单子,然后使用计算表达式将其写为

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

It's worth mentioning as an aside that this

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

is a monadic bind; one could author a 'list' monad and then use computation expressions to write this as

listMonad {
    let! handVal = handValues cards
    let! cardVal = cardValues card
    return hardVal + cardVal
}
清旖 2024-08-29 02:48:24

你做事的方式非常好。可以将列表上的任何递归函数表示为折叠,但我认为在这里这样做不会获得任何好处。也没有内置函数可以完全满足您的需要,但您可以构建一个更通用的函数并在此基础上构建您的特定计算。下面是一种这样的方法:

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]])。我们使用此函数获取手牌中所有可能的值列表,然后对每个此类列表求和。

您可能还有其他几种看待问题的方法,这些方法可能会提出其他变化。

The way you're doing things is perfectly fine. It's possible to express any recursive function on lists as a fold, but I don't think that you gain anything by doing so here. There's also no built-in function to do exactly what you need, but you could build a more generic function and build your specific calculation on top of that. Here's one such approach:

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)

The allChoices function takes a list of lists and returns each possible list containing a single element from each (e.g. allChoices [[1];[2;3];[4;5]] = [[1;2;4];[1;2;5];[1;3;4];[1;3;5]]). We use this function to get all possible lists of values for the cards in a hand, and then sum each such list.

There are probably several other ways you could look at the problem which might suggest other variations.

壹場煙雨 2024-08-29 02:48:24

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

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

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

I think your solution is already good.

Fold does not work in your case. We can fold a list of number, we can also fold two list of numbers. But in your case, it is not simply two lists of numbers.

Consider an extreme case that the your list contains all Aces with length n, then there are 2^n possible values. To enumerate all possibilities, you need a dfs search or bfs search. Your code is actually equivalent to a bfs search (so it costs more memory), although it writes in a recursive way.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文