具有单个构造函数的自引用数据类型不能为 `Show`n

问题描述

这是来自The Little MLer。我有this

data Chain = Link Int (Int -> Chain)

还有这个

ints :: Int -> Chain
ints n = Link (n+1) ints

我对这里到底发生了什么感到很困惑。这似乎是自身的无限递归,其中左侧的 ints 只是无休止地重复整个 ints。书上说

[...] 我们可能会认为 ints 是一个很长的 ints 序列。我们从一个 通过应用第二个组件将此序列中的元素添加到下一个 ints n 到第一个。

我不确定它是如何做到的。但是这会产生一个错误

> ints 0
* No instance for (Show Chain) arising from a use of `print'
:     * In a stmt of an interactive GHCi command: print it

试图追上 deriving Show 不会成功

data Chain = Link Int (Int -> Chain) deriving Show
No instance for (Show (Int -> Chain))
        arising from the second field of `Link' (type `Int -> Chain')
        (maybe you haven't applied a function to enough arguments?)
      Possible fix:
        use a standalone 'deriving instance' declaration,so you can specify the instance context yourself
    * When deriving the instance for (Show Chain)

不确定发生了什么或如何进行。任何类似的例子将不胜感激。


更新

这是 SML 代码

datatype chain = Link of (int * (int -> chain))
fun ints (n) = Link (n+1,ints)
> ints(0)
val it = Link (1,fn) : chain

不完全确定,但 fn 是匿名的 SML 方式,即 fn 是他们的 \。这可能只是巧合。

那么,Haskell 无法处理 SML 的哪些方面呢?这是否与 Haskell 是纯粹的而 SML 不是有关?

解决方法

通常没有好的方法来Show一个函数,所以当涉及到一个函数时,Haskell不会为你制作一个Show的实例..

你可以自己写一个:

instance Show Chain where
  show (Link n fn) = ...

但是现在您必须弄清楚如何显示fn:: Int->Chain。在 Haskell 中,至少函数是原子的和不透明的。您无法拆开它们或检查它们的内容,只能应用它们。

,

这是怎么回事:

current :: Chain -> Int
current (Link i _) = i

next :: Chain -> Chain
next (Link i g) = g i

正如引述所说。你的 ints 是一个函数,所以这里没有无限的事情发生,只是函数应用程序延迟的可能性来获取链中的 next Link 所以下一个 {{1 }} 可以作为 its Int 元素找到。

关于印刷,

current

是一种可能的定义。关于该函数,我们无法提供任何信息,因此我们只能忽略它。

示例交互:

printLink :: Link -> String
printLink (Link i _) = "{Link of " ++ show i ++ " }"

接下来我们可以定义 ch1 = ints 0 i1 = current ch1 -- 1 ch2 = next ch1 i2 = current ch2 -- 2 以便 takeN :: Int -> Chain -> [Int] 从链 takeN 5 ch1 返回五个 Int 的列表,即 ch1

SML 是严格的,它不能像 Haskell 那样有无限列表。因此,SML 代表了一个函数的“下一个”计算,必须显式调用该函数。

由于 Haskell 是懒惰的,我们也只是定义了 [1,2,3,4,5] 来将链转换为其中的无限条目列表,然后在其上使用标准函数 chainToList :: Chain -> [Int]

在您的新 SML 示例中,“take”只是一个指示符,表明存在 a 函数,some 函数。在您的情况下,它是函数 fn 但运行时系统显然不知道这一点。这可能意味着 any 函数将在 SML 中打印为“ints”,如您的示例所示。

但是你不需要打印它来使用它。

,

这是在文本中没有足够远的阅读以及从一种语言 (SML) 而不是另一种语言获得误报的情况。事实证明,让 Chain 返回函数(如 ints)直接输出是一种误用。这些 Chain“生成器”只是作为另一个函数的中介来“查找”这样一个 Chain 对象中的位置,在 TLMLer 的示例中,数据的第二部分中的下一个整数构造函数。

data Chain = Link Int (Int -> Chain)

所以考虑另一个 Chain 生成器 fiveOrsevenInts

mod5or7 n = if (evenDiv n 5) then True else (evenDiv n 7)
                where evenDiv a b = ((a `mod` b) == 0)

fiveOrsevenInts n = if (mod5or7 (n+1))
             then (Link (n+1) fiveOrsevenInts)
             else fiveOrsevenInts (n+1)

fiveOrsevenInts 基于可被 5 或 7 整除的数字创建一个 Chain。据我所知,整个想法是“提前阅读”“链”。所以如果

fiveOrsevenInts (1) => Link 5 fiveOrsevenInts

诀窍是让 fiveOrsevenIntsInt -> Chain 函数)作用于 5 以获取链中的下一个 Int。而且正如我错误地认为的那样,我们根本不需要“看到”上述 fiveOrsevenInts (1) 的直接解析。

在链序列中获取所需位置的一般函数和像 fiveOrsevenInts 这样的链生成函数并返回该位置的成员是

chainItem n (Link i f) = if (n == 1)
                         then i
                         else chainItem (n-1) (f i)

现在,如果我想查看 5 或 7 的整数倍数序列中的第一个

chainItem 1 (fiveOrsevenInts 0)
5
chainItem 2  (fiveOrsevenInts 0)
7
chainItem 3  (fiveOrsevenInts 0)
10
...
chainItem 37  (fiveOrsevenInts 0)
119

fiveOrsevenInts 0 在哪里“播种”Chain。在本章的后面,这个策略被应用于通过构建一个素数 Chain 生成器来查找素数序列。总而言之,我发现 The Little MLer 是一种学习其兄弟 Haskell 的有趣方式。当我完成时,我会尝试分享我的整体翻译。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...