您好我想知道是否有办法以这种方式在haskell中使用do
if
doIfFunction :: Eq a => [a]->[a] ->[a]
doIfFunction l1 l2 = do
if l /= something then (l2++something) else l2
if l /= something2 then (l2++something2) else l2
l2
基本上如果这个函数返回一个不同的值,我希望它将它添加到l2
然后返回l2
。我一直认为l2
是空的,而不应该是,这是因为else l2
是否将l2
的值重置为开始时的值?
如果是这样,我可以做这样的事情吗?
doIfFunction :: Eq a => [a]->[a] ->[a]
doIfFunction l1 l2 = do
if l /= something then (let l2 = l2++something) else l2
if l /= something2 then (let l2 = l2++something2) else l2
l2
这会产生错误,但我想知道这是否在正确的位置。
在调用doIfFunction
时,doIfFunction l []
始终为l
,其中[]
包含值且empty
为gsub
答案 0 :(得分:4)
通常,您应该避免在Haskell中考虑修改任何内容。事实上,你不能,语言不允许它。完全†!
您可以考虑创建l2
的修改版的其他列表,而不是修改列表l2
本身。 (直接的好处是你仍然可以在其他任何地方使用原始的l2
;特别是如果其他一些函数/线程仍然需要这个旧版本并且你不知道它的话就没问题。)
即,而不是
do
modify_something_about_l2
modify_something_else_about_l2
yield_some_result
你想评估函数应用程序链的结果,比如
f l2 = some_other_modification (some_modification (l2))
或者,我们更喜欢写它,
f = some_other_modification . some_modification
在您的特定情况下:
doIfFunction l1
= (\l2 -> if l /= something2 then (l2++something2) else l2)
. (\l2 -> if l /= something then (l2++something) else l2)
如果您不喜欢“向后风格”,您还可以将合成运算符.
替换为其翻转版本:
import Control.Arrow
doIfFunction l1
= (\l2 -> if l /= something then (l2++something) else l2)
>>> (\l2 -> if l /= something2 then (l2++something2) else l2)
此外,您可以通过eta-reduction避免提及中间名单:
doIfFunction l1
= (if l /= something then (++something) else id)
>>> (if l /= something2 then (++something2) else id)
您可能在想:始终创建所有内容的新修改版本,而不仅仅是就地修改,效率很低。好吧,有时它通常实际上没有问题。
您的示例实际上是一个问题:要将something
附加到l2
,需要制作整个列表的副本。也许你可以很容易地避免这个问题:如果不是追加,你 prepend :
doIfFunction l1
= (if l /= something then (something++) else id)
>>> (if l /= something2 then (something2++) else id)
然后没有性能损失。原因是:列表只是一个元素链(头),每个元素都引用了列表的其余部分。 预先 - 添加一个元素只需要创建一个新头并将其链接到现有列表!
† 即使使用聪明的懒惰前置,有时纯功能样式 显着更加耗时。不要担心:虽然Haskell不允许修改值,但有些类型封装了破坏性修改的概念,即ST
monad。因此,你仍然可以有效地实现需要破坏性更新的算法,但基本上你不是在Haskell中,而是在嵌入式“特定于域的命令式语言”中,它与Haskell无缝集成。
答案 1 :(得分:3)
只需将if
子句放在let
语句中:
doIfFunction :: Eq a => [a] -> [a] -> [a]
doIfFunction l1 l2 = do
let l2' = if l /= something then l2 ++ something else l2
let l2'' = if l /= something2 then l2' ++ something2 else l2'
l2''
但是在这种情况下,由于不使用任何monadic操作,因此您不需要do
表示法。我相信你想用do
符号来模拟命令式编程。如果您认为它更具可读性,请考虑使用where
:
doIfFunction :: Eq a => [a] -> [a] -> [a]
doIfFunction l1 l2 = result
where newL2 = if l /= something then l2 ++ something else l2
result = if l /= something2 then newL2 ++ something2 else newL2
或者,当您对Haskell更熟悉时,您可以使用leftaroundabout's answer(这将是写出您想要做的事情的最惯用的方式)。