Haskell替换字符串中字符的更好方法
我做了这个功能
replace :: Char -> Char -> [Char] -> [Char]
replace _ _ [] = []
replace a b (c:r)
| a == c = b : replace a b r
| otherwise = c : replace a b r
当我发现这个时,正在寻找一种更好的方式来编写它:
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map $ c -> if c == a then b else c
甚至没有写第三个参数,我不明白 $ 和 c 符号,但它有效,我想知道这里发生了什么
回答
该$运营商应用一个函数的参数。我们可以将您的示例重写为
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map (c -> if c == a then b else c)
实用上,Haskellers 经常使用$避免括号。由于其他原因,它很少使用。
关于c -> ...:这是一个匿名函数,也称为“lambda”。它代表函数c作为参数并返回...部分。在您的情况下,该函数接受c并检查它是否等于a,在这种情况下返回b,否则返回c。我们可以在没有 lambda 的情况下重写代码,如下所示:
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map myFun
where
myFun :: Char -> Char
myFun = c -> if c == a then b else c
或者,将参数c移到 的左侧=,如下所示:
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map myFun
where
myFun :: Char -> Char
myFun c = if c == a then b else c
关于“缺少第三个参数”:Char -> Char -> [Char] -> [Char]可以通过多种方式读取类型:
- 采用一个参数 (
Char) 并返回一个函数 (Char -> [Char] -> [Char])的函数类型 - 接受两个参数 (
CharandChar) 并返回函数 ([Char] -> [Char])的函数类型 - 接受三个参数 (
Char,Char, 和[Char]) 并返回列表 ([Char])的函数类型
由于柯里化,所有这三种解释都是兼容的。确实,“两个参数”函数
foo :: A -> B -> B
foo x y = y
和功能
foo :: A -> B -> B
foo x = id -- id is the identity function B -> B
是相同的。
在您的示例中,如果需要,您可以将缺少的参数添加到两侧,=如下所示:
replace :: Char -> Char -> [Char] -> [Char]
replace a b xs = map myFun xs
where
myFun :: Char -> Char
myFun c = if c == a then b else c
在这段展开后的代码中,可以看到map myFun xs使用map(library function) 应用于myFunlist 的所有元素xs,并返回所有结果的列表。这有效地实现了您想要的替换。
但是,没有添加第三个参数,
replace :: Char -> Char -> [Char] -> [Char]
replace a b = map myFun
where ...
我们仍然可以解释map myFun为将函数myFun :: Char -> Char转换为函数[Char] -> [Char]。后者确实是replaceif 我们将其解释为“两个参数”函数的返回类型。也就是说,replace 'a' 'b'是一个函数[Char] -> [Char],它接受一个字符串并将其中的每一个都替换'a'为'b'.
回答
甚至没有写第三个参数。(…)
在 Haskell 中,所有函数都只有一个参数。IndeedChar -> Char -> [Char] -> [Char]是 的缩写Char -> (Char -> ([Char] -> [Char])),因此它是一个接受 aChar然后返回另一个函数的函数。如果我们将该函数与 another 一起应用Char,它会返回一个类型[Char] -> [Char]为将Chars列表映射到s 列表的Char函数,最后如果我们使用Chars列表调用该函数,我们将得到一个Chars列表。
这意味着如果函数的头部有两个参数,那map $ c -> if c == a then b else c应该返回一个函数,就是这种情况。
(...) 我不明白
$(...) 符号。
该($) :: (a -> b) -> a -> b函数定义为:
infixr 0 $
($) :: (a -> b) -> a -> b
($) f x = f x
因此,它是功能应用程序。之所以使用它,是因为它的优先级为0,这意味着a $ b x例如将被评估为a (b x),而a b x将被评估为(a b) x。因此,这意味着该函数等价于map (c -> if c == a then b else c)。
我不明白 (...) c 符号
那是一个lambda 表达式。它是一个函数,它将一个变量作为输入,c并将其映射到箭头 ( ->)右侧的部分。因此,这意味着它将Char演员映射c到bif c == a;否则返回c。