【问题标题】:Why does haskell stop when trying to add a string to the end of this string?尝试将字符串添加到该字符串的末尾时,为什么haskell会停止?
【发布时间】:2019-04-15 12:00:42
【问题描述】:

我正在尝试在 Haskell 中的字符串末尾添加一个字符串。

    albumStr = ""
main = do
 let albumStr = albumStr ++ "nothing"
 print albumStr

每当我运行它时,它就会卡住 " 在控制台中,我必须终止它。

为什么?以及如何以这种方式将一个字符串添加到另一个字符串?

编辑: 如何将多个字符串添加到当前字符串的末尾而不覆盖它。

谢谢

【问题讨论】:

  • 请记住,Haskell 中的所有数据都是不可变的——您无法更改变量。当您编写let str = str ++ "nothing" 时,Haskell 将其读取为“构造一个名为str 的新值,它具有str = str ++ "nothing" 的属性”, 换句话说,就是一个永无止境的递归计算。跨度>

标签: string haskell infinite-loop accumulate left-recursion


【解决方案1】:

与 ML 不同,Haskell 没有 rec 关键字来标记递归定义。相反,所有定义都可以递归,这意味着在每个变量定义中,定义的变量在其定义期间已经在范围内。

所以在let albumStr = albumStr ++ "nothing" 中,= 右侧的albumStr 指的是= 左侧定义的那个,而不是第 1 行中定义的那个。因此定义是无限递归的,并且永远循环。

如果你想基于另一个变量定义一个变量,你必须给它一个不同的名字:

let localAlbumStr = albumStr ++ "nothing"

【讨论】:

  • 只有当它是let-binding 时,你才“必须”。但如果是do-binding,则不会。
【解决方案2】:

等式

albumStr = albumStr ++ "nothing"

递归定义一个字符串,根本不使用全局定义的albumStr。递归立即挂起(类似于无限循环)。

打开警告会报告全局名称的阴影。

如果您不想要递归定义,请使用另一个变量名:

albumStr2 = albumStr ++ "nothing"

【讨论】:

  • 问题是我试图在该字符串的末尾添加一堆字符串,所以如果我不使用递归,它只会每次都覆盖。我对 Haskell 很陌生
  • @radrow 同意,即使我宁愿有一个 let nonrec —— 非递归/阴影定义 let x = f x in ... 并不常见(至少在 Haskell 中)。不过,这是一个非常次要的问题。
  • @Deano 建立一个你想附加的所有字符串的列表,然后返回albumStr ++ concat listOfStrings。请记住,您不能修改 albumStr 的全局值(可能您甚至不需要),而只能创建一个新字符串。
【解决方案3】:

do符号中,你可以写

do { albumStr <- pure (albumStr ++ "nothing") 
   ; ... 
   } 

它会(似乎)起作用。

虽然 Haskell 中的 let(和函数)绑定是递归的,但 do 绑定不是。

而是自动创建了一个阴影变量,它实际上是一个同名的新变量。 &lt;- 右边的代码位于绑定上方的范围内; &lt;- 左侧的代码位于一个新的、更广泛的范围内(包括之前的代码)。

另见:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-22
    • 2013-02-02
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多