2012-08-28 2 views
5

Это мое решение осуществлять от Yaht:Может ли этот образец haskell быть короче?

Упражнения 4.6 Напишите тип данных Tuple который может содержать один, два, три или четыре элемента, в зависимости от конструктора (то есть, должно быть четыре Конструкторов , по одному для каждого числа аргументов). Также предоставляйте функции tuple1 через tuple4, которые берут кортеж и возвращают только значение в этом положении, или ничего, если число в действии (т. Е. , вы запрашиваете кортеж4 на кортеже, состоящем только из двух элементов).

Когда я написал первую строчку, я был в восторге от простоты по сравнению с C#

 

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

    -- 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 a b c d → Maybe a 
    tuple2 ∷ Tuplex a b c d → Maybe b 
    tuple3 ∷ Tuplex a b c d → Maybe c 
    tuple4 ∷ Tuplex a b c d → Maybe d 
    tuple1 (Tuple1 a) = Just a 
    tuple1 (Tuple2 a b) = Just a 
    tuple1 (Tuple3 a b c) = Just a 
    tuple1 (Tuple4 a b c d) = Just a 
    tuple2 (Tuple1 a) = Nothing 
    tuple2 (Tuple2 a b) = Just b 
    tuple2 (Tuple3 a b c) = Just b 
    tuple2 (Tuple4 a b c d) = Just b 
    tuple3 (Tuple1 a) = Nothing 
    tuple3 (Tuple2 a b) = Nothing 
    tuple3 (Tuple3 a b c) = Just c 
    tuple3 (Tuple4 a b c d) = Just c 
    tuple4 (Tuple1 a) = Nothing 
    tuple4 (Tuple2 a b) = Nothing 
    tuple4 (Tuple3 a b c) = Nothing 
    tuple4 (Tuple4 a b c d) = Just d 

    -- unit tests 
    prop_tx1 = tuple1 (Tuple1 4) ≡ Just 4 
    prop_tx2 = tuple1 (Tuple2 4 'q') ≡ Just 4 
    prop_tx3 = tuple2 (Tuple1 4) ≡ (Nothing ∷ Maybe Char) 
    prop_tx4 = tuple2 (Tuple2 4 'q') ≡ Just 'q' 

+0

BTW один из C# преимуществ здесь, может быть, по сравнению с Nullable . В C# у нас есть специальный синтаксис (?postfix) и полностью прозрачное (но все же необязательно контролируемое) преобразование между T? и T (int и int). Даже во время распаковки я могу иметь объект x = 5; var y = (int?) x. Не упоминание о ненужном преобразовании из int b = 5; к int? c = b; –

+0

О, так много ответов и путей ... И знаешь что? Ничто не является достаточно удобочитаемым, если принять во внимание то количество знаний, которое автор дает в YAHT в этом упражнении, за исключением первого ответа Даниэля ... Конечно, дальнейшее чтение может принести некоторый свет на «где» или «>> =» или '> =>' ... Но даже этот ответ довольно сложный, я думаю, я бы не догадался, что сам по себе кажется сложным :) Спасибо за ответы много. –

ответ

7

Вот один из способов: централизовать соответствие шаблону.

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 

Тогда tuple1 имеет (среди прочих) типа Tuplex a b c d -> Maybe a, и до tuple4, который имеет (опять же, в числе прочих) тип 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 
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 

Использование имен полей записей, как это на самом деле меньше, чем вы могли бы ожидать в Haskell из-за легкости сопоставления с образцом и , по крайней мере, в некоторых кругах, популярность расширения RecordWildCard, которая позволяет делать такие вещи, как:

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

(при использовании записи диких карт может быть Бетт er, чтобы назвать ваши кортежи чем-то более простым, например tupA, tupB, tupC, tupD)

+7

Другая проблема, по крайней мере в этом примере, заключается в том, что функции доступа являются частичными. Что-то вроде 'tuple2 (Tuple1" foo ")' сломается. В этом конкретном случае - и в большинстве нормальных кодов, я полагаю, вы хотите более элегантный способ обработки недействительных обращений, подобных этому (например, возвращение «Maybe», как указано в вопросе). –

+0

Ах, я не заметил, что он использовал «Может быть». Инструмент 'getive', возможно, сделает это, а если нет, то он должен быть исправлен. Кроме того, есть мало различий в его использовании и 'data Tup abcd = Tup {tA :: (Maybe a), tB :: (Возможно, b) ...}' (плюс некоторые вспомогательные функции/конструкторы), поэтому, возможно, это будет лучшее решение. –

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] 

Edit: Витус правильно указать, что это работает только для однородного кортежа, так что это не правильный ответ. В этом случае я откладываю ответ Даниила.

+1

Если вы не хотите использовать внешний пакет по какой-либо причине, вы также можете сделать что-то вроде 'listToMaybe. drop n'. –

+1

Это работает только в том случае, если все элементы кортежа имеют один и тот же тип. – Landei

+1

Имейте в виду, что 'tuple1 :: TupleX a a a a> Возможно,' а не более общий 'TupleX a b c d -> Может быть''. – Vitus

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 
+0

Это выглядит проще и понятнее, но в момент чтения этой книги я не знаком с оператором «где» –

+0

Это то же самое, что «tuple1 t = let (p, _, _, _) = toMaybes t in p'. В большинстве случаев вы можете видеть 'где' как своего рода« обратный 'let'». Однако есть некоторые отличия в отношении областей и т. Д. – Landei

Смежные вопросы