我有2个列表,我试图填写项目。从stdin读取时,根据读取的其中一个内容的值,我想附加到不同的列表中。例如,
import Control.Monad(replicateM)
main = do
n <- getLine
let l1 = [], l2 = []
in replicateM (read n) (getLine >>= (\line ->
case line of "Yes" ->
-- do something with line
-- and append value of that thing to l1
"No" ->
-- do something else
-- append this value to l2
putStrLn line))
我意识到上面的代码有语法错误等等,但希望你能看到我想要的东西并提出建议。
这是我提出的答案
虽然我们在这里,有人可以解释为什么这给了我一个无限的列表:
let g = []
let g = 1:g
-- g now contains an infinite list of 1's
这是我最终提出的:
import Control.Monad(replicateM)
import Data.Either
getEither::[String] -> [Either Double Double]
getEither [] = []
getEither (line:rest) = let [n, h] = words line
fn = read f :: Double
e = case heist of "Yes" -> Left fn
"No" -> Right fn
in e : getEither rest
main = do
n <- getLine
lines <- replicateM (read n) getLine
let tup = partitionEithers $ getEither lines :: ([Double], [Double])
print tup
不确定在此实例中如何使用fmap
答案 0 :(得分:3)
这是一个简短的ghci会话,可能会给你一些想法:
> :m + Control.Monad Data.Either
> partitionEithers <$> replicateM 3 readLn :: IO ([Int], [Bool])
Left 5
Right True
Left 7
([5,7],[True])
你的第二个问题的答案是,让我们递归;所以g
中的两个let g = 1:g
指的是相同的内存中对象。
答案 1 :(得分:3)
你正在思考可变变量:你是&#34;初始化&#34; l1,l2
到空列表,然后推断使用更长的列表更新它们。这种设计在命令式编程中运行良好,但在纯函数式编程中并不那么简单,因为它涉及到变异。
现在,即使在纯函数式编程中,我们也可以通过monad来模拟变异。例如,曾经可以通过IORefs
或StateT IO
在此处实现变异。但是,在这种情况下,解决任务将是一种不必要的复杂方式。
您想要附加数据以形成两个列表。你想使用replicateM
,这很好。关键是replicateM
只会构建一个列表,而不是两个。现在的问题是:我们如何创建一个容易分成两个的列表?
第一个丑陋的尝试是生成标记值列表,即对列表:
case line of
"Yes" -> let value = ... in
return ("for l1", value)
"No" -> let value = ... in
return ("for l2", value)
这样做会使replicateM
生成一个列表,例如
[("for l1", value1), ("for l1", value2), ("for l2", value3), ...]
然后我们可以分成两个列表。
对标签使用字符串看起来有点不雅,因为布尔值就足够了:
case line of
"Yes" -> let value = ... in
return (True, value)
"No" -> let value = ... in
return (False, value)
更好的方法是使用Either a b
类型:
case line of
"Yes" -> let value1 = ... in
return (Left value1)
"No" -> let value2 = ... in
return (Right value2)
上述优点是value1
和value2
甚至可以是不同的类型。之前的片段迫使他们分享他们的类型:因为我们构建了一对对,所以每对必须具有相同的类型。现在,新列表不是[Either a b]
类型,其中a
是要放在l1
中的值的类型,b
是l2
的值。
获得[Either a b]
后,您希望将其拆分为[a]
和[b]
。正如@DanielWagner在他的回答中建议的那样,你可以利用partitionEithers
。