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
一旦在尾部命中一个不等于头部的值,就会返回false
order
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
一旦在尾部命中一个不等于头部的值,就会返回false
order
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
必须等待整个列表被读入后才能返回任何内容。唯一的