#ByteString や Text なら uncons 関数を使うという手もありますね。 http://t.co/tbLwfbsS http://t.co/uy0gVINv #import qualified Data.ByteString as BS
foo bs =
case BS.uncons bs of
Nothing -> undefined
Just (x,xs) ->
case BS.null xs of
True -> undefined
_ -> undefined
#import qualified Data.Text as T
foo txt =
case T.uncons txt of
Nothing -> undefined
Just (x,xs) ->
case T.null xs of
True -> undefined
_ -> undefined
#> foo を ByteString や Text で書き直す
#綺麗にみえるように uncons 関数を使って最初に処理を分岐する形にしていますけれど、実際には null 関数を使って最初に分岐するようにした方が良いですね。
#view パターンを使うと、こんな感じでしょうか。
#{-# LANGUAGE ViewPatterns #-}
import qualified Data.Text as T
foo (T.null -> True) = undefined
-- Just to avoid warning about non-exhaustive pattern ...
foo (T.uncons -> Nothing) = undefined
foo (T.uncons -> Just (x, xs)) =
case T.null xs of
True -> undefined
_ -> undefined
#{-# LANGUAGE ViewPatterns #-}
import Data.Maybe (fromJust)
import qualified Data.Text as T
foo (T.null -> True) = 1
foo (T.uncons -> T.null . snd . fromJust -> True) = undefined
foo txt = undefined
#あっ、 s/1/undefined/ です。
#ありがとうございます。最初に null で分岐する心はなんでしょうか? あと、view の最初のコードだと、uncons は 2 回評価されてしまうのでしょうか?
#あっ、uncons を使って空かどうか確かめても、null を使って空かどうか確かめても同じですね。杞憂でした。 http://t.co/smiffbPA http://t.co/PPXMN5ak #uncons で空かどうか確かめるよりも、null で空かどうか確かめた方がコストが低くなると考えたためです。ただ、さっき書いたように杞憂でした……。 > 最初に null で分岐する心
#-ddump-ds してみれば分かると思いますが、uncons が評価されるのは最初の一度だけですね。
#Result size = 33
Test.foo :: forall a_ajs. Data.Text.Internal.Text -> a_ajs
[LclIdX]
Test.foo =
\ (@ a_aju) (ds_djE :: Data.Text.Internal.Text) ->
case Data.Text.null ds_djE of _ {
__DEFAULT ->
(\ _ ->
case Data.Text.uncons ds_djE of _ {
Data.Maybe.Nothing -> GHC.Err.undefined @ a_aju;
Data.Maybe.Just ds_djH ->
case ds_djH of _ { (_, xs_afu) ->
case Data.Text.null xs_afu of _ {
__DEFAULT ->
(\ _ -> GHC.Err.undefined @ a_aju) GHC.Prim.realWorld#;
GHC.Types.True -> GHC.Err.undefined @ a_aju
}
}
})
GHC.Prim.realWorld#;
GHC.Types.True -> GHC.Err.undefined @ a_aju
}
#なお、desugar のされ方こそ違うものの、最適化結果は全て同じコードになるようです。(GHC 7.4.1)