如何在Haskell中编写多行字符串?

89

假设我有一个带有换行符的字符串文字:

file :: String
file = "the first line\nthe second line\nthe third line"

有没有办法像这样写?

file :: String
file = "the first line
        the second line
        the third line"

上面的尝试会导致这个错误:

factor.hs:58:33:
    lexical error in string/character literal at character '\n'
Failed, modules loaded: none.

1
假设有一种方法,它不会添加空格以实现缩进吗? - Jonathan Fischoff
@JonathanFischoff 请查看我的回答 - Nikita Volkov
6个回答

120

你可以像这样编写多行字符串

x = "This is some text which we escape \
      \   and unescape to keep writing"

打印出来的样子是

"This is some text which we escape   and unescape to keep writing"

如果您希望此内容打印成两行

x = "This is some text which we escape \n\
      \   and unescape to keep writing"

打印出来的结果是

This is some text which we escape
    and unescape to keep writing

48

前不久,我发布了一个叫做"neat-interpolation"的库来解决多行字符串和使用QuasiQoutes扩展的插值问题。 它相对于竞争对手的主要优势是智能空格管理,可以处理插入的多行字符串。 以下是它的工作原理示例。

执行以下操作:

{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}

import NeatInterpolation (text)
import qualified Data.Text.IO

f :: Text -> Text -> Text
f a b = 
  [text|
    function(){
      function(){
        $a
      }
      return $b
    }
  |]

main = Data.Text.IO.putStrLn $ f "1" "2"

将生成以下内容(请注意,缩进比声明时减少):

function(){
  function(){
    1
  }
  return 2
}

现在让我们使用多行字符串参数来测试它:

main = Data.Text.IO.putStrLn $ f 
  "{\n\
  \  indented line\n\
  \  indented line\n\
  \}" 
  "{\n\
  \  indented line\n\
  \  indented line\n\
  \}"

我们得到

function(){
  function(){
    {
      indented line
      indented line
    }
  }
  return {
    indented line
    indented line
  }
}

注意它如何完美地保留了变量占位符所在行的缩进级别。标准的内插器会破坏所有的空白并产生类似以下内容的结果:

    function(){
      function(){
        {
  indented line
  indented line
}
      }
      return {
  indented line
  indented line
}
    }

1
有趣的是,但不支持 GHC 7.4.x,因为它需要一些语言扩展(MultiWayIfLambdaCase)。 - MathematicalOrchid
3
是的。在我的大多数库中,我不支持早于GHC 7.6的版本。 - Nikita Volkov

30
在Haskell中,你可以通过在字符串末尾加上反斜杠 \ 并用另一个反斜杠 \ 开始新行来输入多行字符串,就像这样:
file :: String
file = "the first line\n\  
    \the second line\n\  
    \the third line\n"  

这并不像 OP 想要的那样添加换行符。 - Jonathan Fischoff
1
你需要显式地添加换行转义字符才能使其工作。按照你的写法,这是不起作用的。 - Varun Madiath
当然,抱歉。我更专注于向您展示如何在Haskell中添加多行字符串,以至于忘记写'\n'了。现在已经修复了。谢谢。 - nschoe
不错。在我看来,这是最佳答案,因为它不仅展示了该怎么做,而且还解释了它的一般工作原理。真的很好。 - TobiMcNamobi

24

我不相信Haskell有一种简单的方法可以做到这一点,而不必诉诸于准引用或其他方法。然而,您可以通过像下面这样使用unlines函数来大部分获得您想要的效果。但是,这将导致您的最后一行之后有一个换行符,这可能对您是否在意取决。

file :: String
file = unlines [
    "the first line",
    "the second line",
    "the third line"
    ]

4
这会增加运行时间成本,而像我或nschoe的答案中使用的文本常量则不会有这种情况。 - daniel gratzer
3
我知道这一点。我只是阅读了核心部分,希望 GHC 会优化掉那个调用,但并没有那样的运气。不过我在几个地方都看到了这种模式。 - Varun Madiath
1
就我所知,我认为这是最常见的模式。 - Jonathan Fischoff
6
这可能是最佳的解决方案。如果运行成本是问题的话,那么模板Haskell可以解决它。如果你不想要尾随的换行符,请使用intercalate "\n"代替unlines - Thomas Eding
5
如果您像Thomas Eding建议的那样使用intercalate,请记得导入Data.List模块。我猜测导入模块是为什么我经常看到使用unlines而不是intercalate的要求。 - Varun Madiath
8
“过早优化是万恶之源”,由于惰性求值,运行时成本非常小且仅为O(1),如果您对此存在性能问题,不要使用[Char],而应该使用ByteString或Text。 - Daniel Hill

12

9

Haskell中的多行字符串中展示了一个准引用示例。

{-# LANGUAGE QuasiQuotes #-}
import Text.RawString.QQ

multiline :: String
multiline = [r|<HTML>
<HEAD>
<TITLE>Auto-generated html formated source</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
</HEAD>
<BODY LINK="800080" BGCOLOR="#ffffff">
<P> </P>
<PRE>|]

"

raw-strings-qq是我目前最喜欢的用于此目的的工具。

"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接