【问题标题】:racket: pattern matching function parameters球拍:模式匹配功能参数
【发布时间】:2016-07-13 17:17:17
【问题描述】:

我正在阅读《球拍领域》一书,第 175 页,我实际上看到了这段代码:

(struct dice-world (src board gt))
(struct game (board player moves))

(define (no-more-moves-in-world? w)
  (define tree (dice-world-gt w))
  (define board (dice-world-board w))
  (define player (game-player tree))
  player)

书中的函数不会返回播放器,但该行之前的所有内容都与书中一样。对我来说,这表明需要模式匹配!

确实,这很好用,对我来说更具可读性和明确性:

(define (no-more-moves+? w)
  (match w
    [(dice-world _ board (game _ player _)) player]))

但是在这里我们仍然毫无理由地将变量命名为w。我希望能够直接在函数参数上进行模式匹配,如下所示(无效语法):

(define (no-more-moves2? (dice-world _ board (game _ player _)))
  player)

到目前为止,从我的谷歌搜索来看,这似乎是不可能的?这听起来让我难以置信?我猜这可能是通过一些宏技巧来实现的,但我真的很惊讶这不是从书中编写代码的标准方法?作为初学者的书,我想也许以后会介绍模式匹配,但我在索引中根本找不到?

另外,如果答案是不可能/不是惯用的,我想知道其他 lisps 是否也一样?

【问题讨论】:

    标签: pattern-matching racket


    【解决方案1】:

    为了完整起见,还有define/match,在#lang racket 中可用,它比Alexis 的解决方案略冗长,比正文中的match 略少冗长。

    #lang racket
    (struct dice-world (src board gt))
    (struct game (board player moves))
    
    (define/match (no-more-moves-in-world? w)
      [((dice-world _ _ (game _ p _))) p])
    

    【讨论】:

    • 啊,是的,我可能应该在回答中提到这一点,哈哈——define/match* 末尾的* 是为了避免与define/match 发生冲突。
    【解决方案2】:

    您想要的表格可从 Racket match-plus package, under the name define/match* 获得。免责声明:这是我的包裹。以下代码使用该模块应该可以正常工作:

    (require match-plus)
    
    (define/match* (no-more-moves2? (dice-world _ board (game _ player _)))
      player)
    

    要安装包,请使用 DrRacket 的包管理器 GUI,或运行以下命令:

    raco pkg install match-plus
    

    我认为这个 not 被包含在 Racket 本身中的理由正如 John Clements 所提到的:使用这种形式,如果模式匹配失败,函数只会抛出异常。尽管如此,这对您的用例还是非常有用的,尤其是当您在函数上签订合同时。如文档所述:

    ...只能指定一个匹配子句,因为匹配模式与形式定义是内联的。这意味着如果模式匹配失败会引发错误,但如果函数已经以保证模式匹配成功的方式进行收缩,这是理想的。

    (强调我的。)

    【讨论】:

    • 感谢您的包裹!会用,不过还没测试。我还不是很清楚。合同是运行时检查,对吗?不是编译时?那么合约的优势是更好的运行时异常错误消息?因为显然no-more-moves-in-world?,虽然更冗长,但如果你给它一个字符串,就会抛出异常......
    • @EmmanuelTouzery 正确。合约让您编码各方必须履行的保证,否则将引发错误。如果您使用 Typed Racket,您也可以在编译时检查其中的许多保证,并且此包应该也可以与 Typed Racket 一起使用,并具有类似的好处。
    • 一直在思考。模式匹配的一个缺点是,如果您更改结构,例如添加一个新字段,它就会崩溃。在 lisps 中没有编译时错误,另外,也许开发风格鼓励对数据结构进行更流畅的工作(随着开发的进展进行更多的修改)。这听起来也是避免模式匹配的好理由。
    • @EmmanuelTouzery 如果您添加一个新字段,这应该无法编译。另一方面,如果您重新排序字段,或者重命名它们,这仍然会编译。如果您想防止这种情况发生,请使用 struct* 匹配扩展器按名称而不是按位置提供字段。这将为您提供最强的更改鲁棒性(以增加一些冗长为代价)。
    【解决方案3】:

    是的,你绝对可以编写这个宏。我认为这将是您示例的长度,可能更短。

    那么,为什么这不是惯用语?我认为基本答案是这样的:如果不匹配会发生什么?如果你想提供多种模式怎么办?我猜你可能来自 Haskell 背景,所有的模式匹配定义都合并成一个函数。我认为Scheme对于打破“一个完整的函数定义”还是很谨慎的 被包裹在一对括号中,”更广泛地说,“范围是分隔的 用括号括起来。”确实,这在球拍中变得有点松散了——每 lambda 现在可以在其中立即定义定义——但我认为一般来说 “味道”将是使用您的第二种形式,在 lambda 中匹配 带有命名参数。

    但是,嘿——这就是宏的用途:使用您认为最易读的形式!我会成为 如果您愿意,很乐意为您提供该宏。

    【讨论】:

    • 哈哈,haskell 搞砸了!好的我明白了。 Alexis King 提供了一个现成的包,所以我会使用它。与如果不匹配怎么办有关——如果参数不是预期的,no-more-moves-in-world? 已经爆炸了,所以?.. 但我问了亚历克西斯回答的后续问题。谢谢!
    猜你喜欢
    • 2014-03-16
    • 2023-03-21
    • 2014-07-06
    • 2020-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多