1

我有一个简单的程序(这是CCC 2012第二个问题),它需要一个数字列表,并确定是否有任何严格的增加/减少/常量序列进行。例如:Haskell:懒惰影响解析方法

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,在我甚至完成之前就已经给出了答案!我认为这是一个错误,但后来我愚蠢地认识到,哈斯克尔的懒惰(我认为?)自己决定,在我输入1,2,3,0后,无论发生什么后,结果都会是Nothing,所以很高兴输出。

不幸的是,当我改变

let readings = map (read :: (Read a, Num a) => String -> a) $ lines input 

let readings = parse $ 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" 
+1

+1表示顿悟的启示;)。 – gphilip 2012-03-04 08:23:33

回答

5

如果所有条目甫一,all isJust entries检查项的完整列表,这意味着项目的整个列表需要先阅读parse才能返回。

好吧,为什么orders是懒惰的更长解释 - all只要达到谓词返回False的值就会返回False。因此,constant只要它在尾部的值不等于头部就立即返回false。 order返回constant,因此order是懒惰的。

我的第一个建议是风格 - 当计算orders时,请看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 

请注意,你需要通过Just xJust yNothing如果一方或双方在导入Data.Monad

liftM2 compare将返回Just (compare x y)其参数是Nothing

orders现在是[Maybe Ordering]。如果orders是恒定的(注:(==)作品上Maybe S)与第一个元素是Just,返回的第一个元素(这已经是一个Maybe Ordering)。否则,只需返回Nothing。你可以做无isJust (head orders)电话,但加入应该让它尽快恢复,因为它看到了Nothing(否则,如果你给它的所有Nothing s的列表,它会检查是否每一个Nothing)。

+0

我以前会这样做,但问题是可以让不好意思的输入悄无声息。如果我输入'1','2','efyugf'和'3',那么整个事情应该被扔掉,而不是解释为1-2-3。 – mk12 2012-03-04 06:16:22

+0

您是正确的,它表现出与以前相同的行为时,我进行此更改。有没有办法做我想要的,同时避免上述? – mk12 2012-03-04 06:18:26

+0

问题是如果你输入'1','2','1','efuas'会发生什么? 'parse'不能返回任何东西,因为它可能需要返回'[]'。如果它返回“1:2:1:列表的其余部分”,然后点击“efuas”,它将不得不“退回”列表的最初部分,这甚至没有意义。正因为如此,'parse'必须等到整个列表被读入后才返回任何内容。我可以看到的唯一解决方案是将所有内容都移动到相同的函数中,或放宽错误检查约束。 – Retief 2012-03-04 06:24:08

2

您可以使用mapMaybeData.Maybe。也就是说,交换map readmapMaybe maybeReadmapMaybe所做的是将函数映射到列表上,过滤掉Nothing并提取所有剩余的值。

+0

你说得对,就像雷蒂夫所说的那样,只比做我自己的'地图'和'过滤器是唯一的'更清洁。但它仍然不理想。 – mk12 2012-03-04 06:35:15

+0

它是一个很好的建议 - 比我的'从$ filter $ is $ map map f'的地图好得多,或者是风格上的列表理解。 – Retief 2012-03-04 06:47:13