有没有任何Haskell函数可以使用分隔符连接列表?

154
有没有一个函数可以用分隔符将列表中的元素连接起来? 例如:
> foobar " " ["is","there","such","a","function","?"]
["is there such a function ?"]

感谢任何回复!


17
我知道Lmgtfy回答不好,但值得注意的是,在Hoogle上搜索“String -> [String] -> String”可以得到你想要的结果。http://www.haskell.org/hoogle/ - sigfpe
4
对于使用空格连接的情况,你也可以使用 unwords 函数。 - epsilonhalbe
1
@sigfpe 旁注:如果另一种方式没有返回答案,你将不得不寻找“[String] -> String -> String”,对吧? - Lay González
1
@LayGonzález 搜索涉及排列组合。例如,搜索 [a] -> (a -> b) -> [b] 会返回 map 作为其第一个结果。 - gallais
5个回答

266

是的,这个函数:

Prelude> import Data.List
Prelude Data.List> intercalate " " ["is","there","such","a","function","?"]
"is there such a function ?"

intersperse函数更为通用:

Prelude> import Data.List
Prelude Data.List> concat (intersperse " " ["is","there","such","a","function","?"])
"is there such a function ?"

此外,对于想要使用空格字符连接的特定情况,可以使用unwords

Prelude> unwords ["is","there","such","a","function","?"]
"is there such a function ?"

unlines 的工作方式类似,不同的是字符串使用换行符来合并,并在结尾添加了一个换行符。 (这使它适用于序列化文本文件,在 POSIX 标准中必须以一个尾随换行符结尾)


它中的任何部分能够处理可能为空的字符串吗? - CMCDragonkai
3
@CMCDragonkai,我不确定你具体指的是什么,但是这些函数都允许将任意字符串用作分隔符和元素。例如,intercalate "," ["some", "", "string"] = "some,,string"intercalate "" ["foo", "bar"] = "foobar" - Niklas B.
3
unlines 函数会给每一行添加一个换行符,比如 unlines ["A", "B"] = "A\nB\n",因此它与 intercalate 函数是不同的。请注意,翻译过程中不修改原意,也不提供额外的解释或其他内容。 - Kathy Van Stone
@KathyVanStone 很有趣,我猜我从来没有尝试过,只是假设它的工作方式类似于“unwords”。 - Niklas B.
1
很好,标准库中有一些正常的字符串和列表操作函数,并且您在此发布示例是很好的,因为在Haskell中找到这种日常编程的任何文档都相当困难。 - Andrew Koster
有没有办法在intersperse中添加谓词?如果条件满足,则在列表中的每个项目之间插入“ ”。 - plasma

7

使用foldr编写一行代码并不难。

join sep xs = foldr (\a b-> a ++ if b=="" then b else sep ++ b) "" xs
join " " ["is","there","such","a","function","?"]

6
最好加上一些描述;有人标记它为低质量。 - Arya McCarthy
1
这个折叠的概念有点像 reduce(如果你知道 reduce 是什么的话)。Foldr(fold right)对列表中的每个元素应用一个函数,并将结果添加到累加器中。然后它返回累积值。在这里,我们声明了一个函数 join,它接受一个分隔符和一个字符串列表。然后它使用匿名 lambda 函数在列表上应用 fold。在 (\a b-> a ++ if b=="" then b else sep ++ b) 中,a 是累加器,b 是列表中的下一个项目。折叠从 "" 开始。 - trevdev

4

如果有人感兴趣,可以实现 intersperse 和 intercalate 的其他想法:

myIntersperse :: a -> [a] -> [a]
myIntersperse _ [] = []
myIntersperse e xs = init $ xs >>= (:[e])

myIntercalate :: [a] -> [[a]] -> [a]
myIntercalate e xs = concat $ myIntersperse e xs

xs >>= f 等同于 concat (map f xs)


3
joinBy sep cont = drop (length sep) $ concat $ map (\w -> sep ++ w) cont

3
如果您想编写自己的intercalateintersperse函数:
intercalate :: [a] -> [[a]] -> [a]
intercalate s [] = []
intercalate s [x] = x
intercalate s (x:xs) = x ++ s ++ (intercalate s xs)

intersperse :: a -> [a] -> [a]
intersperse s [] = []
intersperse s [x] = [x]
intersperse s (x:xs) = x : s : (intersperse s xs)

1
为什么要局限于字符串呢?另外,你对函数应用周围的括号是多余的。 - melpomene
真的,intersperse 不需要是 Strings,但是 intercalate 至少需要是 Show,如果你确实使用了 Show,你肯定需要一个方法来处理它们并使用 Strings。我仍在适应 Haskell 如何处理混合中缀和前缀函数 / 运算符,如果我最终想要使用 $,我更喜欢在混合时加括号。 - Zoey Hewll
intercalate :: [a] -> [[a]] -> [a] - 为什么需要 Show?至于语法,Haskell没有任何前缀运算符(除了“-”,它是一种可憎的东西),而函数应用比任何中缀运算符都紧密:x:s:intersperse s xs 是可以的(但如果你加上空格,它读起来会好得多:x : s : intersperse s xs(我真的不明白为什么人们喜欢在 : 周围留出空格)。 - melpomene
对了,我总是忘记字符串就是列表这一事实。使用“显示”仅因为我以为你想要结果为“字符串”。 我所指的“中缀和前缀函数/操作符”,其实是“前缀函数和中缀操作符”,但那样说可能不够清晰。一元负号很危险。至于“:”和其他中缀操作符,我是否在它们之间使用空格高度依赖于上下文,但我在局部保持一致。例如,在模式匹配中,“(:)”从来不会有空格,但在其他情况下,是否有空格取决于它是否被括起来以及我的心情。 - Zoey Hewll

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