【问题标题】:How to define a function in ghci across multiple lines?如何在 ghci 中跨多行定义一个函数?
【发布时间】:2010-05-17 00:16:14
【问题描述】:

我正在尝试在ghci中定义任何跨越多行的简单函数,以以下为例:

let abs n | n >= 0 = n
          | otherwise = -n

到目前为止,我已经尝试在第一行之后按 Enter:

Prelude> let abs n | n >= 0 = n
Prelude>           | otherwise = -n
<interactive>:1:0: parse error on input `|'

我也尝试过使用:{:} 命令,但我没有走多远:

Prelude> :{
unknown command ':{'
use :? for help.

我在 Linux 上为 Haskell 98 使用 GHC Interactive 6.6 版,我缺少什么?

【问题讨论】:

  • 请升级您的 GHC 安装。 GHC 6.6 已经快 5 年了!最新版本的 Haskell 在这里:haskell.org/platform
  • @Mark 这个 OP 已经尝试过解决该问题的方法。这个问题是由于过时的 ghci,而不是缺乏对做什么的了解。解决方案:升级。那里的解决方案:使用:{:}

标签: haskell ghci multiline-repl-definition


【解决方案1】:

GHCi 现在有一个多行输入模式,通过 :set +m 启用。例如,

Prelude> :set +m
Prelude> let fac 0 = 1
Prelude|     fac n = n * fac (n-1)
Prelude|
Prelude> fac 10
3628800

【讨论】:

  • 设置多行模式使ghci 在这方面的行为很像 Python 解释器。很方便!事实上,您可以在您的主目录中创建一个.ghci 文件,并在其中放置:set +m,并且每次启动ghci 时,多行模式将成为默认模式!
  • 这真是太棒了。但我注意到,当我使用:set prompt "λ " 设置提示时,续行会显示Prelude 而不是λ。有什么办法可以解决这个问题?
  • 见这里补丁定义一个新的延续提示ghc.haskell.org/trac/ghc/ticket/7509#no1
  • 为了防止 Prelude 出现在续行中,还要在你的 .ghci 中添加 :set prompt2 "|"。
  • 您可以完全使用结尾的let 避免缩进。只需键入 let 后跟换行符:let ⏎。然后fac 0 = 1⏎。然后fac n = n * fac (n-1)⏎⏎你就完成了!
【解决方案2】:

对于守卫(如您的示例),您可以将它们全部放在一条线上并且它可以工作(守卫不关心间距)

let abs n | n >= 0 = n | otherwise = -n

如果您想编写具有多个与参数模式匹配的定义的函数,如下所示:

fact 0 = 1
fact n = n * fact (n-1)

然后您将使用带有分号的大括号分隔定义

let { fact 0 = 1 ; fact n = n * fact (n-1) }

【讨论】:

    【解决方案3】:

    Dan 是正确的,但 :{:} 必须各自出现在自己的行中:

    > :{ 
    > let foo a b = a +
    >           b
    > :}
    > :t foo
    foo :: (Num a) => a -> a -> a
    

    这也与布局规则交互,因此在使用 do-notation 时,显式使用大括号和分号可能更容易。例如,此定义失败:

    > :{
    | let prRev = do
    |   inp <- getLine
    |   putStrLn $ reverse inp
    | :}
    <interactive>:1:18:
        The last statement in a 'do' construct must be an expression
    

    但是在添加大括号和分号时它可以工作:

    > :{
    | let prRev = do {
    |   inp <- getLine;
    |   putStrLn $ reverse inp;
    | }
    | :}
    > :t prRev
    prRev :: IO ()
    

    这只有在从文件中粘贴定义时才真正重要,因为缩进可能会改变。

    【讨论】:

    • 如果您有一行以 '=' 结尾(定义在下一行),这不起作用,至少在版本 7.6.3 中。
    • 也许这失败了,因为 let 的第二和第三行缩进不够......? (另外两个空格。)
    【解决方案4】:

    看起来:{:} 是一个相当新的功能。您可能需要升级 GHC。

    编辑:确认,见http://www.haskell.org/ghc/docs/6.8.2/html/users_guide/release-6-8-2.html

    【讨论】:

      【解决方案5】:

      如果您不想只为:{:} 升级GHC,则需要将它们全部写在一行上:

      > let abs' n | n >= 0 = n | otherwise = -n
      

      我不知道 Haskell 中有任何一个定义必须写在多行上。以上确实在 GHCi 中有效:

      > :t abs'
      abs' :: (Num a, Ord a) => a -> a
      

      对于其他表达式,例如do 块,您需要使用带有大括号和分号的非布局语法(呃)。

      【讨论】:

        【解决方案6】:

        看起来像一次粘贴两行或对每个新行使用 control-enter 将它们保持在一起,至少在 https://repl.it/languages/haskell 处。您会在第二行的开头看到 2 个点。或者把它放在一个文件中并 :load 文件 (:l main)。为什么 abs 不适用于负数?哦,你必须在数字周围加上括号。

           let abs n | n >= 0 = n 
        ..           | otherwise = -n
        
           abs (-1)
        

        【讨论】:

          【解决方案7】:

          我在 macOS Catalina 10.15.2 上使用 GHCi,版本 8.2.1。以下是我如何将函数类型声明和守卫放在一起。请注意,左侧的垂直条用于 GHCi 多行。

          λ: let abs' :: (Num a, Ord a) => a -> a
           |     abs' n | n >= 0 = n | otherwise = -n
           | 
          λ: abs' 7
          7
          λ: abs' (-7)
          7
          

          【讨论】:

          • 如果你使用:{:},你不需要在你的类型声明之前指定let ,这意味着你不需要缩进第二行和后续行。
          • 非常感谢大卫。这正是我一直在寻找但未能找到的东西。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-08-12
          • 1970-01-01
          • 2016-11-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多