5

これはYAHTから運動するための私の解決策です:

演習 4.6 コンストラクタに応じて、1、2、3、または 4 つの要素を保持できるデータ型タプルを記述します (つまり、引数の数ごとに 1 つずつ、4 つのコンストラクタが必要です)。また、タプルを取り、その位置の値のみを返す関数 tuple1 から tuple4 も提供します。数値が有効な場合は Nothing を返します (つまり、2 つの要素のみを保持するタプルで tuple4 を要求します)。

最初の行を書いたとき、C# に比べてシンプルであることに興奮しました

    データ Tuplex abcd = Tuple1 a | Tuple2 ab | Tuple3 abc | Tuple4 abcd

    -- class Tuplex<a,b,c,d> {
    -- Tuplex(a p1){ _p1 = p1; }
    -- Tuplex(a p1, b p2){ _p1 = p1; _p2 = p2; }
    -- Tuplex(a p1, b p2, c p3){ _p1 = p1; _p2 = p2; _p3 = p3; }
    -- Tuplex(a p1, b p2, c p3, d p4){ _p1 = p1; _p2 = p2; _p3 = p3; _p4 = p4; }
    -- public Nullable<a> _p1;
    -- public Nullable<b> _p2;
    -- public Nullable<c> _p3;
    -- public Nullable<d> _p4;
    -- }

C#だとどのフィールドにも問題なくアクセスできますが、ここでは「アクセサ関数」を書くべきですよね?そして、ここのコードの量は私を悲しくさせます。

ここで短いコードを使用できますか?

    tuple1 ∷ Tuplex abcd → たぶん a
    tuple2 ∷ Tuplex abcd → たぶん b
    tuple3 ∷ Tuplex abcd → たぶん c
    tuple4 ∷ Tuplex abcd → たぶん d
    tuple1 (Tuple1 a) = ただ a
    tuple1 (Tuple2 ab) = ただ
    tuple1 (Tuple3 abc) = ただ
    tuple1 (Tuple4 abcd) = ただ
    tuple2 (Tuple1 a) = なし
    tuple2 (Tuple2 ab) = ただ b
    tuple2 (Tuple3 abc) = ただ b
    tuple2 (Tuple4 abcd) = ただ b
    tuple3 (Tuple1 a) = なし
    tuple3 (Tuple2 ab) = なし
    tuple3 (Tuple3 abc) = ただ c
    tuple3 (Tuple4 abcd) = ただ c
    tuple4 (Tuple1 a) = なし
    tuple4 (Tuple2 ab) = なし
    tuple4 (Tuple3 abc) = なし
    tuple4 (Tuple4 abcd) = ただ d

    -- 単体テスト
    prop_tx1 = tuple1 (Tuple1 4) ≡ ちょうど 4
    prop_tx2 = tuple1 (Tuple2 4 'q') ≡ ちょうど 4
    prop_tx3 = tuple2 (Tuple1 4) ≡ (Nothing ∷ Maybe Char)
    prop_tx4 = tuple2 (Tuple2 4 'q') ≡ 'q' のみ

4

4 に答える 4

7

1 つの方法を次に示します。パターン マッチを集中化します。

unTuplex f1 f2 f3 f4 t = case t of
    Tuple1 a       -> f1 a
    Tuple2 a b     -> f2 a b
    Tuple3 a b c   -> f3 a b c
    Tuple4 a b c d -> f4 a b c d

tuple1 = unTuplex (\a -> Just a ) (\a _ -> Just a ) (\a _ _ -> Just a ) (\a _ _ _ -> Just a)
tuple2 = unTuplex (\_ -> Nothing) (\_ b -> Just b ) (\_ b _ -> Just b ) (\_ b _ _ -> Just b)
tuple3 = unTuplex (\_ -> Nothing) (\_ _ -> Nothing) (\_ _ c -> Just c ) (\_ _ c _ -> Just c)
tuple4 = unTuplex (\_ -> Nothing) (\_ _ -> Nothing) (\_ _ _ -> Nothing) (\_ _ _ d -> Just d)

または、ネストされた構造を明示的に表現することもできます。

{-# LANGUAGE NoMonomorphismRestriction #-}
data DONE = DONE -- could just use (), but this is a pretty descriptive name
type Tuplex a b c d = Maybe (a, Maybe (b, Maybe (c, Maybe (d, DONE))))

tuple1 x = x >>= return . fst -- or tuple1 = fmap fst
tuple2 x = x >>= tuple1 . snd
tuple3 x = x >>= tuple2 . snd
tuple4 x = x >>= tuple3 . snd

次に、tuple1has (とりわけ) type Tuplex a b c d -> Maybe a、および up to tuple4which has (再び、とりわけ) type Tuplex a b c d -> Maybe d

編集: ...実際には、これは最初のアプローチの別の継続を示唆しています。

import Control.Monad

decrement :: Tuplex a b c d -> Maybe (Tuplex b c d t)
decrement (Tuple1 a) = Nothing
decrement (Tuple2 a b) = Just (Tuple1 b)
decrement (Tuple3 a b c) = Just (Tuple2 b c)
decrement (Tuple4 a b c d) = Just (Tuple3 b c d)

zero :: Tuplex a b c d -> a
zero (Tuple1 a) = a
zero (Tuple2 a b) = a
zero (Tuple3 a b c) = a
zero (Tuple4 a b c d) = a

tuple1 = Just . zero
tuple2 = decrement >=> tuple1
tuple3 = decrement >=> tuple2
tuple4 = decrement >=> tuple3
于 2012-08-28T13:18:52.580 に答える
7

私はそれを非常に単純にしようとします:

data Tuplex a b c d = Tuple1 a | Tuple2 a b | Tuple3 a b c | Tuple4 a b c d

toMaybes (Tuple1 p)       = (Just p, Nothing, Nothing, Nothing)
toMaybes (Tuple2 p q)     = (Just p, Just  q, Nothing, Nothing)
toMaybes (Tuple3 p q r)   = (Just p, Just  q, Just  r, Nothing)
toMaybes (Tuple4 p q r s) = (Just p, Just  q, Just  r, Just  s)

tuple1 t = p where (p,_,_,_) = toMaybes t 
tuple2 t = q where (_,q,_,_) = toMaybes t 
tuple3 t = r where (_,_,r,_) = toMaybes t 
tuple4 t = s where (_,_,_,s) = toMaybes t
于 2012-08-28T20:52:49.423 に答える
1

タプルにフィールド名を付けるだけです!

data Tuplex a b c d = Tuple1 { tuple1 :: a }
                    | Tuple2 { tuple1 :: a
                             , tuple2 :: b }
                    | Tuple3 { tuple1 :: a
                             , tuple2 :: b
                             , tuple3 :: c }
                    | Tuple4 { tuple1 :: a
                             , tuple2 :: b
                             , tuple3 :: c
                             , tuple4 :: d }

その結果、次のタイプの関数があります。

tuple1 :: Tuplex a b c d -> a
tuple2 :: Tuplex a b c d -> b
-- etc

RecordWildCardこのようなレコードのフィールド名を使用することは、パターンマッチングが容易であり、少なくとも一部のサークルでは、次のようなことができる拡張機能が人気があるため、Haskellで予想されるよりも実際には一般的ではありません。

function (Tuples3 {..}) =
-- now you have variables tuple1 :: a, tuple2 :: b, etc.

(レコードワイルドカードを使用する場合は、タプルフィールドにtupA、tupB、tupC、tupDなどの少し単純な名前を付ける方がよい場合があります)

于 2012-08-28T05:14:21.110 に答える
1
import Safe (atMay) -- from the 'safe' package

toList (Tuple1 a) = [a]
toList (Tuple2 a b) = [a, b]
toList (Tuple3 a b c) = [a, b, c]
toList (Tuple4 a b c d) = [a, b, c, d]

tuple n t = atMay (toList t) n

[tuple1, tuple2, tuple3, tuple4] = map tuple [1..4]

編集:Vitusは、これは同種のタプルに対してのみ機能することを正しく指摘しているため、これは正しい答えではありません。その場合、私はダニエルの答えに従います。

于 2012-08-28T16:05:44.937 に答える