4

次のようなばかげたものがあるとします。

data SomeType
    = Unary Int
    | Associative SomeType
    | Binary SomeType SomeType

some_func :: SomeType -> Int
some_func s =
    case s of
        Unary n -> n
        Associative s1 -> some_func s1
        Binary s1 s2 -> some_func s1 + some_func s2

ここで some_func は、指定された SomeType 内のすべての SomeTypes を調べ、すべての単項データ コンストラクターの Int を合計します。SomeType は再帰的なデータ型です。

これらのパターン マッチでは、 を繰り返してsome_func s1います。@、when、let、またはその他のものをsf1 = some_func s1使用して宣言し、両方で使用する方法はありますか? このようなもの:

data SomeType
    = Unary Int
    | Associative SomeType
    | Binary SomeType SomeType

some_func :: SomeType -> Int
some_func s =
    case s of
        Unary n -> n
        Associative s1 -> sf1
        Binary s1 s2 -> sf1 + sf2
        where
            sf1 = some_func s1
            sf2 = some_func s2

ここでの問題は、s1 と s2 が後のブロックでのみ認識され->、sf1 が計算できないことです。

4

5 に答える 5

7

これは質問には答えませんが、問題を解決するかもしれません:

{-# LANGUAGE DeriveFoldable #-}
module SomeType where
import Data.Foldable as F

data SomeType a
    = Unary a
    | Associative (SomeType a)
    | Binary (SomeType a) (SomeType a)
      deriving (Foldable)

some_func :: SomeType Int -> Int
some_func = F.foldl1 (+)
于 2012-04-21T08:20:13.637 に答える
5

レコード構文を乱用してください!

data SomeType
    = Unary { u :: Int }
    | Associative { s1 :: SomeType }
    | Binary { s1, s2 :: SomeType }

someFunc :: SomeType -> Int
someFunc s = case s of
    Unary{}       -> u s
    Associative{} -> sf1
    Binary{}      -> sf1 + sf2
  where
    sf1 = someFunc (s1 s)
    sf2 = someFunc (s2 s)

同じ型の異なるコンストラクターは、それらのレコードに同じ名前のフィールドを持つことができることに注意してください。sf2怠惰は、ブランチをたどる場合にエラーが発生するのを防ぎますAssociative

于 2012-04-21T15:58:48.043 に答える
5

答えはノーです。inはs1inAssociativeとは異なります。それらが異なるコンテキストに存在するため、それらが同じ名前を持っているという事実は気を散らすものです。s1Binary

ヘルパーを使用して入力を節約できると思いますが、これは繰り返されるロジックをカプセル化するのに実際には役立ちません。

some_func :: SomeType -> Int
some_func s = go s
  where go (Unary n) = n
        go (Associative s1) = go s1
        go (Binary s1 s2) = go s1 + go s2
于 2012-04-21T07:26:57.970 に答える
4

この特定のケースでこれが短くなるかどうかはわかりませんが、より一般的なケースでは、Scrap Your Boilerplateをチェックしてください。例えば:

{-# LANGUAGE Rank2Types, DeriveDataTypeable, NoMonomorphismRestriction #-}

import Data.Generics

data SomeType
    = Unary Int
    | Associative SomeType
    | Binary SomeType SomeType
    deriving (Data, Typeable, Show)

flatten_sometype x@(Unary _) = x
flatten_sometype (Associative s) = s
flatten_sometype (Binary (Unary a) (Unary b)) = Unary $ a + b

some_func2 = let Unary n = everywhere (mkT flatten_sometype)
             in  n

ご覧のとおり、 を使用everywhereすると、ローカル変換を指定するだけで済みます。SYB がすべての再帰を処理します。これが本当に役立つのは、複数の型が関係している場合です。SYB は、変換していない型を喜んでトンネリングし、それらの引数も変換します。ただし、使用量には注意してください。使用しすぎると、大量の GC チャーンが発生する可能性があります。

于 2012-04-21T08:11:47.770 に答える
0

それを書くための最短の方法は、単にパターンマッチングであるべきです:

some_func :: SomeType -> Int
some_func (Unary n) = n
some_func (Associative s) = some_func s
some_func (Binary s1 s2) = (some_func s1) + (some_func s2)

まだ断片の繰り返しがあるので、おそらくあなたの質問に答えないでしょう...多分何かsome_funcの観点から定義することを含む何かfmap some_func

于 2012-04-21T09:03:23.847 に答える