【问题标题】:How should the cloned sheep type be constructed克隆羊类型应该如何构建
【发布时间】:2020-06-12 10:55:42
【问题描述】:

所以在Haskell monads的解释中,他们举了克隆羊的例子。 然而,它们只给出了数据类型的部分实现:

type Sheep = ...

father :: Sheep -> Maybe Sheep
father = ...

mother :: Sheep -> Maybe Sheep
mother = ...

这使得实验和理解诸如 comb 之类的 monad 的用法变得相当困难:

-- comb is a combinator for sequencing operations that return Maybe
comb :: Maybe a -> (a -> Maybe b) -> Maybe b
comb Nothing  _ = Nothing
comb (Just x) f = f x

Sheep应该如何实现?

【问题讨论】:

  • 在文章中,重点是无所谓类型如何实现,只要定义了这两个函数即可。
  • @FyodorSoikin 这听起来绝对合乎逻辑然而对我来说重要的是尝试它并真正理解它的重要性。因此,我可以跳出的定义将有助于我的理解。我想这归结为我仍然难以阅读类型定义(例如m a -> (a -> m b) -> m b),而实际实现有助于获得更多感觉。至于给出的答案,给定的定义是type,答案是data,我还不明白这是怎么发生的。
  • data 中的 "data" 是 "datatype" 的缩写。 data 不创建数据,它创建数据类型。 ( Haskell 中无数错误命名选择的又一个案例

标签: haskell monads


【解决方案1】:

这个答案是为了解决您在 cmets 中给出的说明:

给定的定义是type,答案是data,我还不明白这是怎么发生的。


Haskell 有几种不同的声明类型的方式。

type 关键字用于声明 类型别名 - 类似于 C++ 中的 typedefusing。理论上,作为一种徒劳的练习,可以将Sheep 声明为String 的别名,隐含的理解是String 将表示羊的名字,然后有一个“全球”父母名单每只羊,motherfather 函数将用于查找:

type Sheep = String

allParents :: [(Sheep, (Sheep, Sheep))]
allParents =
  [ ("dolly", ("rufus", "peggy"))
  , ("peggy", ("ramses", "woolly"))
  , ...
  ]

mother :: Sheep -> Maybe Sheep
mother s = fst <$> lookup s allParents

father :: Sheep -> Maybe Sheep
father s = snd <$> lookup s allParents

但这将是非常不寻常的,根本没有功能。我认为这将是 gorilla holding a banana and the entire jungle 的一个典型例子。

对这种情况进行建模的更 Haskell 方法是将 Sheep 声明为具有(至少)两个字段的数据结构 - motherfather。对于这种事情,使用关键字data

data Sheep = Sheep (Maybe Sheep) (Maybe Sheep)

father :: Sheep -> Maybe Sheep
father (Sheep f _) = f

mother :: Sheep -> Maybe Sheep
mother (Sheep _ m) = m

但是为你的类型的每个字段定义这些访问器函数是很普通的,所以 Haskell 有一个更短的语法:

data Sheep = Sheep { father :: Maybe Sheep, mother :: Maybe Sheep }

? 这将声明具有两个字段的相同结构,plus 会自动创建与前面示例中的工作方式相同的访问器函数。

为了抢占您的下一个问题,我还应该提一下定义类型的第三种方式 - newtype,类似于 data,但仅适用于只有一个字段的结构,并且只是一种性能优化。


不要纠结于使用的特定关键字。这篇文章只需要指出存在这种类型和两个函数,它们是如何定义的并不重要。文章不得不用some关键字来说明这一点,他们选择了type。或多或少的任意选择。

【讨论】:

    【解决方案2】:

    Sheep 的一个最小实现是

    data Sheep = Sheep {mother :: Maybe Sheep, father :: Maybe Sheep}
    

    一个更理智的实现,你可以有兄弟姐妹(可以通过他们的name来区分)

    data Sheep = Sheep {name :: String, mother :: Maybe Sheep, father :: Maybe Sheep}
    

    【讨论】:

    • 所以正如我所读到的,这是在声明一个包含母亲、父亲和姓名的“数据结构”(对不起 C++ 程序员),对吗?但它使用data 声明而不是wiki 的type 声明。
    • @TarickWelling: type 用于类型同义词datanewtype 用于定义新类型。
    【解决方案3】:

    Sheep 的定义真的无关紧要。 使用重要的是motherfather 的定义。以下将起作用

    type Sheep = Int
    
    mother :: Sheep -> Maybe Sheep
    mother 0 = Nothing
    mother 1 = Nothing
    mother n = Just (n `div` 2)
    
    father :: Sheep -> MaybeSheep
    father 0 = Nothing
    father 1 = Nothing
    father n = Just (n `mod` 2)
    

    应该

    type Sheep = String
    
    mother :: Sheep -> Maybe Sheep
    mother (x:_:rest) = Just [x]
    mother _ = Nothing
    
    father :: Sheep -> Maybe Sheep
    father (_:x:rest) = Just [x]
    father _ = Nothing
    

    (在这两种情况下,我都不太关心motherfather 的含义;它说明类型很重要。)

    comb 本身实际上并不关心 motherfather 返回什么;它只是处理试图找到非绵羊的母亲或父亲的问题。您可以将motherfathercomb 一起使用,只要它们的类型正确即可。

    comb (Just name) mother == mother name -- name :: Sheep, so mother name :: Maybe Sheep
    comb Nothing mother == Nothing -- Nothing :: Maybe Sheep in both cases
    

    【讨论】:

    • 也许吧。我并没有真正考虑过这个名字的结构。 (如果是你父母全名的串联,你必须把名字分成两半,父母是每一半的第一个字符。或者什么。)
    • 啊,对。切换到Just [x] 以避免过多考虑函数的实际工作方式,因为类型检查是问题的重点。
    • 我建议(x:rest) 的原因是,只有[x] 链接的应用程序mother &gt;=&gt; mother 肯定会失败。 :)
    猜你喜欢
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多