2012-08-28 16 views
5

To jest moje rozwiązanie do wykonywania z Yaht:Czy ta próbka skrótu może być krótsza?

Ćwiczenia 4.6 Napisz krotka typ danych, który może pomieścić jedną, dwie, trzy lub cztery elementy, w zależności od konstruktora (czyli nie powinno być cztery konstruktorów , po jednym dla każdej liczby argumentów). Udostępnia także funkcje od tuple1 do tuple4, które pobierają krotkę i zwracają tylko wartość w tej pozycji, lub Nic, jeśli liczba jest poprawna (tj. , żądasz tuple4 na krotce zawierającej tylko dwa elementy).

Kiedy napisałem pierwszy wiersz byłem podekscytowany prostotą w porównaniu do 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; 
    -- } 

W języku C# mam dostępu do żadnego pola bez problemu, ale tutaj mam napisać „funkcje dostępowe”, prawda? I ilość kodu tutaj mnie zasmuca.

Czy mogę podać tutaj krótszy kod?

 

    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 jednym z C# zalet Oto Może w porównaniu do Nullable . W języku C# mamy specjalną składnię (?postfix) i całkowicie przezroczystą (ale nadal opcjonalnie kontrolowaną) konwersję między T? i T (int? i int). Nawet podczas rozpakowywania mogę mieć obiekt x = 5; var y = (int?) x. Nie wspominając o niepotrzebnej konwersji z int b = 5; do int? c = b; –

+0

Och, tyle odpowiedzi i sposobów ... I wiesz co? Nic nie jest dostatecznie czytelne, jeśli weźmiemy pod uwagę ilość wiedzy, którą autor daje w YAHT w tym punkcie ćwiczeń, z wyjątkiem pierwszej odpowiedzi Daniela ... Oczywiście, dalsze czytanie może rzucić trochę światła na "gdzie" lub ">>" lub '> =>' operatorzy ... Ale nawet ta odpowiedź jest dość skomplikowana, myślę, że nie zgadłbym, że przeze mnie, wydaje się podchwytliwa :) Bardzo dziękuję za odpowiedzi. –

Odpowiedz

7

Oto jeden sposób: scentralizować dopasowanie wzorca.

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) 

Alternatywnie, można jawnie wyrazić zagnieżdżony strukturę:

{-# 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 

Następnie tuple1 ma (między innymi) typu Tuplex a b c d -> Maybe a, i dalej aż do tuple4 który ma (ponownie, między innymi) typ Tuplex a b c d -> Maybe d.

edytuj: ... faktycznie, sugeruje to alternatywną kontynuację pierwszego podejścia.

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

Po prostu podaj swoje nazwy pól kępek!

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 } 

I w rezultacie masz funkcje z typów:

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

Używanie nazw pól rekordów, jak to jest w rzeczywistości mniej powszechne niż można się było spodziewać w Haskell ze względu na łatwość dopasowywania wzorców i , przynajmniej w niektórych kręgach, popularność rozszerzeniem RecordWildCard który pozwala robić takie rzeczy jak:

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

(przy użyciu rekordu dzikie karty może być Bett er, aby nazwać swoje pola krotka nieco prostsze, takie jak tupA, tupB, tupC, tupD)

+7

Innym problemem, przynajmniej w tym przykładzie, jest to, że funkcje akcesora są częściowe. Coś takiego jak 'tuple2 (Tuple1" foo ")' pęknie. W tym konkretnym przypadku - i w większości normalnego kodu, jak sądzę - potrzebujesz bardziej eleganckiego sposobu radzenia sobie z nieważnymi dostępami w ten sposób (np. Zwracając "Być może", jak określono w pytaniu). –

+0

Ah, nie zauważyłem, że użył "Może". Narzędzie "wyprowadzić" prawdopodobnie może to zrobić, a jeśli nie, to powinno zostać załatane. Poza tym nie ma różnicy w jego używaniu i 'danych Tup abcd = Tup {tA :: (Maybe a), tB :: (Maybe b) ...}' (plus niektóre funkcje pomocnicze/konstruktory) więc może to byłoby lepsze rozwiązanie. –

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: Wita słusznie podkreślają, że to działa tylko dla jednorodnej krotki, więc to nie jest prawidłowa odpowiedź. W takim przypadku odsuwam się od odpowiedzi Daniela.

+1

Jeśli nie chcesz używać zewnętrznego pakietu z jakiegokolwiek powodu, możesz również zrobić coś takiego jak 'listToMaybe. upuść n'. –

+1

Działa to tylko wtedy, gdy wszystkie elementy krotki mają ten sam typ. – Landei

+1

Pamiętaj, że 'tuple1 :: TupleX a a a a -> Może a', a nie bardziej ogólne' TupleX a b c d -> Maybe a'. – Vitus

7

chciałbym spróbować zachować to martwy prosta:

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

To wygląda na prostsze i zrozumiałe, ale w momencie czytania tej książki nie jestem obeznany z operatorem "gdzie" –

+0

To jest to samo co 'tuple1 t = let (p, _, _, _) = toMaybes t in p'. W większości przypadków możesz zobaczyć "gdzie" jako rodzaj "odwróconego" let ". Istnieją jednak pewne różnice dotyczące zakresów itp. – Landei

Powiązane problemy