【问题标题】:Understanding Haskell (<-) operator了解 Haskell (<-) 运算符
【发布时间】:2017-07-05 15:49:18
【问题描述】:

我在https://rosettacode.org/wiki/Zebra_puzzle#LP-like_version 研究代码时有些困惑。

这是该代码中发生的事情的一个最小示例。

import Control.Monad
import Data.List

values :: (Bounded a, Enum a) => [[a]]
values = permutations [minBound..maxBound]

data Nation = English | Swede | Dane
  deriving (Bounded, Eq, Enum, Show)

data Color = Red | Blue | Green
  deriving (Bounded, Eq, Enum, Show)

answers = do

  nation <- values
  begin nation English

  color <- values
  end color Red

  return $ zip nation color

  where
    end xs x = guard $ last xs == x
    begin xs x = guard $ head xs == x

main :: IO ()
main = do
    forM_ answers $ (\answer ->  -- for answer in answers:
      do
        mapM_ print answer
        putStrLn "----" )
    putStrLn "No more solutions!"

对我来说神秘的是 do 块中发生的事情。

我从各种来源了解到a &lt;- b 运算符执行b 操作并将其绑定到a。但是这里的动作到底是什么?

我以为它是values,但如果我注释掉beginend 函数调用和定义,那么haskell 就不再知道如何处理values。我想这是因为他们定义中的相等性测试是让 haskell 推断类型的原因(我错了吗?)。

  1. 在那种情况下,动作到底是什么?
  2. 还有values 如何知道将permutations 应用于哪个有界类型?这部分似乎很神秘。
  3. 最后,如果我想保持不变 代码如上,但修改它列出所有排列(不仅仅是 满足beginend) 我该怎么做?

【问题讨论】:

  • 从技术上讲,它不是运算符;它只是将&gt;&gt;= 运算符的结果绑定到隐式函数中的名称的语法。 a &lt;- b 被解析为b &gt;&gt;= (\a -&gt; ...),其中...do 块的其余部分。
  • 如果没有beginend(它们的相等性测试确实可以指导类型推断,因为只有与== 相同的东西才能被测试),类型检查器没有办法看到你在谈论Nations 和Colors。它只是看到你想要BoundedEnumShow 这两种类型,并且非常正确地拒绝猜测你的意思。
  • 哦该死的列表 Monad。是的,这并不是最直接的例子。
  • 列表理解会更符合这一点。这种风格可能用于模仿逻辑编程,但它本身并不是一种很好的风格(IMO)。 (我也发现do..where 很糟糕的风格)

标签: haskell


【解决方案1】:

您可以非常机械地将do 表示法转换为列表理解表示法:

answers = [ zip nation color
          | nation <- values, head nation == English
          , color <- values, last color == Red
          ]

我真的不喜欢 headlast 的业务。

【讨论】:

  • 谢谢。这有帮助。对于 (3),在代码中添加 answers :: [[(Nation, Color)]] 有助于列出所有没有 nationcolor 的排列。对于 (1) 我现在将其理解为一堆嵌套动作。所以只有 (2) 需要澄清。
  • 我想答案的类型签名也有助于值知道要作用于哪个有界类型。哦,好吧……有些时候这似乎是纯粹的魔法。
  • @IvanAbraham,好吧,answers 甚至没有类型签名。类型推断有时确实看起来很神奇,但实际上并非如此。要记住的关键是类型信息可以在各个方向流动。在color &lt;- values 中,color 值由values 值确定,但values 类型由color 类型确定。编译器只是通过称为 unification 的相当标准的过程传播它发现的等式,以计算出要正确匹配所有内容的类型必须是什么。然而,为了帮助您理解您的代码,它是
  • 通常最好为所有顶级函数和所有非平凡的本地函数提供类型签名。有时您需要为后者使用 ScopedTypeVariables 扩展名。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-05
  • 1970-01-01
  • 2011-02-19
  • 2016-03-13
  • 2010-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多