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