【问题标题】:The last statement in a 'do' block must be an expression'do' 块中的最后一条语句必须是表达式
【发布时间】:2020-03-01 11:26:26
【问题描述】:

正如标题中所说:“do”构造中的最后一条语句必须是表达式是什么意思?我知道还有其他科目与我类似,但我仍然找不到我的错误。

代码:

import System.Environment

check_args argc args i = do
        putStrLn $ show (args !! i)
        i <- return (i + 1)
        if argc > i
                then mydisplay2 argc args i
                else return i

loop_args argc args = do
        let i = 0
        i <- check_args argc args i

main = do
        args <- getArgs
        putStrLn "The arguments are:"
        loop_args (length args) args

【问题讨论】:

  • i &lt;- check_args argc args i 没有任何意义,因为您不会对 i 做任何事情。您可以将其重写为 check_args argc args i。但在我看来,您的目标是在 Haskell 中以一种命令式的方式进行编程。请注意,通常不会使用那么多 doreturn 不是关键字,而是一个函数,其工作方式与您在命令式语言中所期望的不同。
  • 好的,谢谢您的回答。我是 Haskell 的新手,我完全迷路了,我想做的是一一检查 argv,然后比较它们。
  • 我强烈推荐 Learn You A Haskell 作为最好的和最易读/易懂的语言介绍。 This chapter 将与您的当前需求最相关,但我强烈建议您也阅读前面的章节,特别是要了解纯函数式编程与命令式编程的不同之处。
  • 如果您要尝试对 do 块进行脱糖,您将得到 check_args args args i &gt;&gt;= \i -&gt; :一个没有正文的 lambda 表达式。
  • @chepner 另一种表明它的方式是,根据 Monad 定律,任何 do 块 do { .... ; A } 都等同于 do { .... ; r &lt;- A ; return r }r &lt;- i &lt;- .... 是毫无意义的错误语法。

标签: haskell syntax


【解决方案1】:

问题出在loop_args函数:

loop_args argc args = do
        let i = 0
        i <- check_args argc args i

i &lt;- … 是没有意义的,因为你在这里定义了一个名为i 变量(这与let i = 0 中的i 没有太大关系),并且您根本不使用该变量。您可以用check_args argc args i 替换它,错误就会消失,但它可能仍然无法按照您希望的方式工作。

但总的来说,程序看起来命令式,而 Haskell 是一种声明式语言。这意味着每个变量(所以i)都是不可变的,因此您可以通过递增一个变量来循环。此外,在声明性语言中,没有太多“循环”等。目的不是指定如何你想做什么,而是你想做什么

您可能希望在这里使用可以在Foldable 对象中移动的mapM_ :: (Foldable f, Monad m) =&gt; (a -&gt; m b) -&gt; f a -&gt; m (),并将一元函数应用于所有项目:

main = do
    args <- getArgs
    putStrLn "The arguments are:"
    mapM_ putStrLn args

【讨论】:

  • 感谢您的回答。例如,如何使用此 mapM 检查我的参数之一是否等于“foo”
  • @Sanka:您可以使用filter 过滤项目列表,或使用any 检查列表中的任何项目是否满足给定谓词。但我认为首先尝试一些简单的表达式可能会有所帮助(目前不适用于do 上下文)。 Monad 是一个非常有表现力的概念,但不幸的是,它需要一些经验才能习惯。
  • “现代”Haskell 是 traverse_ 而不是 mapM_(尽管您需要导入它 import Data.Foldable (traverse_))。
  • Haskell 不是声明性的,它有很多循环。甚至还有一个用于 monad 循环的包,称为 monad-loops。当然,通过手动索引编码循环,是的,递增是多余的。 mapM_ 也是一个杂牌;映射意味着将列表转换为新的东西,但在这里它纯粹是为了副作用。表达该意图的名称是for_
  • @WillNess:for_ 循环是用flip traverse_ 构造的。此外,有循环不会使某些东西成为非声明性的。例如 Kotlin 中的 for 循环机制肯定比 Java 中的更具有声明性,因为在循环中更新迭代器对下一次迭代没有影响。从仍然存在“控制”(不仅仅是数据)的意义上说,Haskell 当然不是一种声明性语言。但我认为大多数人都会同意它比 Java 更具声明性:)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多