Parsing Haskell:受解析方法影响的惰性
我有一个简单的程序(这是第二个问题),它获取一个数字列表,并确定是否存在严格的递增/递减/常数序列。例如:Parsing Haskell:受解析方法影响的惰性,parsing,haskell,input,functional-programming,lazy-evaluation,Parsing,Haskell,Input,Functional Programming,Lazy Evaluation,我有一个简单的程序(这是第二个问题),它获取一个数字列表,并确定是否存在严格的递增/递减/常数序列。例如: 1 2 3 4 7 8 => Increasing 5 1 -2 -100 => Decreasing 9 9 9 9 9 9 => Constant 1 2 3 4 5 0 => Nothing 当我编写这段代码时,哈斯克尔是多么聪明,我完全被震惊了。出于某种原因,当我在stdin中以交互方式输入数字时,in甚至在我完成之前就给了我答案!我以为这是一个bug,但
1 2 3 4 7 8 => Increasing
5 1 -2 -100 => Decreasing
9 9 9 9 9 9 => Constant
1 2 3 4 5 0 => Nothing
当我编写这段代码时,哈斯克尔是多么聪明,我完全被震惊了。出于某种原因,当我在stdin中以交互方式输入数字时,in甚至在我完成之前就给了我答案!我以为这是一个bug,但后来我愚蠢地意识到哈斯克尔的懒惰(我想是吧?)让我自己决定,在我输入1
、2
、3
、0
,不管之后发生什么,结果都是什么都没有,所以它很高兴地输出了这个结果
不幸的是,当我改变
let readings = map (read :: (Read a, Num a) => String -> a) $ lines input
到
parse
是一种更安全的读取数字输入的方法,实现如下
maybeRead :: (Read a) => String -> Maybe a
maybeRead = fmap fst . listToMaybe . filter (null . dropWhile isSpace . snd) . reads
parse :: (Read a) => [String] -> [a]
parse xs =
let entries = map maybeRead xs
in if all isJust entries
then map fromJust entries
else []
它不再这样做了
为什么?
编辑:更多代码
-- | Zip together adjacent list elements as pairs in a new list.
zipPairs :: [a] -> [(a, a)]
zipPairs xs = zip (init xs) (tail xs)
-- | Return True if all elements of a given list are equal.
constant :: (Eq a) => [a] -> Bool
constant xs = all (== head xs) (tail xs)
-- | Return the order that the elements of a list are sorted in, if they form
-- a strictly increasing (Just LT), decreasing (Just GT) or constant (Just EQ)
-- sequence. If there is no pattern, return Nothing.
order :: (Ord a) => [a] -> Maybe Ordering
order xs =
let orders = map (\(x, y) -> x `compare` y) (zipPairs xs)
in if constant orders then Just (head orders) else Nothing
然后在main
中
let readings = parse $ lines input
putStrLn $ if null readings
then "bad input"
else case order readings of
Just EQ -> "Constant"
Just LT -> "Diving"
Just GT -> "Rising"
Nothing -> "Nothing"
如果所有条目都是just,all isJust entries
检查整个条目列表,这意味着在parse
返回之前需要读入整个条目列表
好的,进一步解释一下为什么orders
是懒惰的--all
在到达谓词返回的值False
时返回False
。因此,constant
一旦在尾部命中一个不等于头部的值,就会返回falseorder
在constant
返回时立即返回,因此order
是惰性的
我的第一个建议是风格——在计算订单时,查看zipWith
函数let orders=zipWith compare xs$tail xs
应该同样有效
至于解决你的实际问题,试试看
order xs = let orders = zipWith (liftM2 compare) xs $ tail xs
in if isJust (head orders) && constant orders
then head orders
else Nothing
请注意,您需要导入数据.Monad
liftM2 compare
在传递Just x
和Just y
时将返回Just(compare x y)
,如果其中一个或两个参数都是Nothing
订单
现在是一个[可能是订单]
。如果orders
是常量(注意:(==)
在Maybe
s上工作),并且第一个元素是一个刚刚
,则返回第一个元素(已经是Maybe Ordering
)。否则,只需返回Nothing
。您可以不使用isJust(headorders)
调用,但是添加它应该使它在看到Nothing
时立即返回(否则,如果您给它一个所有Nothing
的列表,它将检查是否每个条目都是Nothing
)。如果所有条目都是just,all isJust entries
检查整个条目列表,这意味着在parse
返回之前需要读入整个条目列表
好的,进一步解释一下为什么orders
是懒惰的--all
在到达谓词返回的值False
时返回False
。因此,constant
一旦在尾部命中一个不等于头部的值,就会返回falseorder
在constant
返回时立即返回,因此order
是惰性的
我的第一个建议是风格——在计算订单时,查看zipWith
函数let orders=zipWith compare xs$tail xs
应该同样有效
至于解决你的实际问题,试试看
order xs = let orders = zipWith (liftM2 compare) xs $ tail xs
in if isJust (head orders) && constant orders
then head orders
else Nothing
请注意,您需要导入数据.Monad
liftM2 compare
在传递Just x
和Just y
时将返回Just(compare x y)
,如果其中一个或两个参数都是Nothing
订单
现在是一个[可能是订单]
。如果orders
是常量(注意:(==)
在Maybe
s上工作),并且第一个元素是一个刚刚
,则返回第一个元素(已经是Maybe Ordering
)。否则,只需返回Nothing
。您可以不使用isJust(headorders)
调用,但是添加它应该会使它在看到Nothing
时立即返回(否则,如果您给它一个所有Nothing
的列表,它将检查是否每个人都是Nothing
。您可能可以使用数据中的映射mappey
。也就是说,将map read
与map maybead
交换。mappay
所做的是将函数映射到列表上,过滤掉Nothing
s并提取所有剩余值 您可能可以使用数据中的映射mamaybe
。Maybe
。也就是说,将map read
与map maybead
交换。mappay
所做的是将函数映射到列表上,过滤掉Nothing
s并提取所有剩余值 我以前打算这么做,但问题是这样会让错误的输入悄悄地消失,而不被注意。如果我输入1
、2
、efyugf
和3
,那么整个事情都应该扔掉,而不是解释为1-2-3。您是正确的,当我进行此更改时,它表现出与以前相同的行为。在避免上述情况的同时,是否没有办法做我想做的事情?问题是如果您输入1
,2
,1
,efuas
<代码>解析
无法返回任何内容,因为它可能需要返回[]
。如果它返回列表的1:2:1:rest
,然后点击efuas
,它将不得不“取消”列表的初始部分,这甚至没有意义。因此,parse
必须等待整个列表被读入后才能返回任何内容。唯一的