2012-01-25 13 views
6

Pracuję nad problemem pisania kodu Haskella podobnego do programu C++.Przeciążanie funkcji narzędzi w Haskell

kod C++ to:

class Rectangle 
{ 
    private: 
     int length; 
     int width; 
    public: 
     Rectangle() 
     { 
      length = 0; 
      width = 0; 
     } 
     Rectangle(int x) 
     { 
      length = x; 
      width =0; 
     } 
     Rectangle (int x , int y) 
     { 
      length = x; 
      width = y; 
     } 
}; 

Aby napisać podobny kod Haskell zrobiłem typ danych Prostokąt

data Rectangle = Rectangle Length Width deriving (Eq, Show , Read) 
type Length = Int 
type Width = Int 

potem pomyślałem popełnienia funkcji obciążenia, które mogą działać jako konstruktora. Ale nie rozumiem, jak zaimplementować przeciążanie funkcji różnymi argumentami. Proszę o pomoc. Dzięki.

+1

Nie zawracałbym sobie głowy modelowaniem haskell po C++; to po prostu doprowadzi do dużego bólu. Dlaczego drugi konstruktor prostokąta jest linią? Myślę, że bardziej sensowną implementacją byłoby domyślne (1, 1), a jeśli otrzymasz jedną int, podaj (x, x), aby utworzyć kwadrat. – alternative

+0

Słyszałeś o liście inicjalizacyjnej konstruktora? –

+2

@ VladLazarenko: Czy słyszałeś o ludziach, którzy wciąż się uczą? (Podpowiedź: jesteś wśród nich, wszyscy są) –

Odpowiedz

21

Choć jest możliwe do zrobienia takiego przeciążenia w Haskell, nie jest uważane za idiomatyczne, i prawdopodobnie będzie prowadzić do błędów mylące później. Zamiast tego, należy po prostu określić funkcje, które konstruują dane:

point :: Rectangle 
point = Rectangle 0 0 

line :: Length -> Rectangle 
line l = Rectangle l 0 

square :: Int -> Rectangle 
square a = Rectangle a a 

To pozwala dać jasne nazwy, które opisują semantykę każdego przeciążeniem, zamiast polegania na liczbę i rodzaj argumentów danego disambiguate którego oznaczać.

Jednakże, jeśli chcesz napisać wersję przeciążony, można to zrobić z łatwością typeclasses:

class MakeRectangle a where 
    rectangle :: a 

instance MakeRectangle Rectangle where 
    rectangle = Rectangle 0 0 

instance MakeRectangle (Length -> Rectangle) where 
    rectangle l = Rectangle l 0 

instance MakeRectangle (Length -> Width -> Rectangle) where 
    rectangle = Rectangle 

Musisz {-# LANGUAGE FlexibleInstances #-} w górnej części pliku do kompilowania to. Taka sztuczka jest używana przez standardową bibliotekę Text.Printf, ale nie uważam jej za szczególnie dobry przykład przeciążania w Haskell; prawie zawsze istnieje pewna struktura dla typu przeciążonej wartości, podczas gdy tutaj cała jego struktura jest dyktowana przez instancję, która może stać na drodze do wnioskowania o typie; nie tylko to, ale nie ma żadnych rozsądnych praw, które rządzą instancjami (w rzeczywistości typ jest zbyt ogólny, aby można było na to zezwolić).

Ale jeśli naprawdę chcesz to zrobić, możesz i choć zwykle jest to zły pomysł, czasami (jak w przypadku printf) jest to jedyny sposób na osiągnięcie pożądanego interfejsu.

Aby to wypróbować w GHCi, musisz określić typy których używasz jednoznacznie, czy to nie będzie w stanie rozwiązać instancje:

GHCi> rectangle :: Rectangle 
Rectangle 0 0 
GHCi> rectangle (1 :: Length) :: Rectangle 
Rectangle 1 0 
GHCi> rectangle (1 :: Length) (2 :: Width) :: Rectangle 
Rectangle 1 2 
+0

Nie jestem w stanie skompilować programu. Błąd to "wielokrotne deklaracje dla prostokąta" –

+0

Czy upewniłeś się zachować wcięcie? Czy masz inną deklarację prostokąta w innym miejscu pliku? Jeśli nie jest to żadna z nich, dodaj cały plik, który kompilujesz, i zobaczę. – ehird

+1

Zapomniałeś uwzględnić spacje z mojego kodu; Haskell jest językiem wrażliwym na wcięcia. – ehird

22

Możesz użyć składni rekordu, aby osiągnąć to zachowanie.

data Rectangle = Rectangle {len :: Length, width :: Width} deriving (Eq, Show , Read) 
type Length = Int 
type Width = Int 

rectangle = Rectangle { len = 0, width = 0 } 

rectangle :: Rectangle będzie twoim konstruktorem tutaj.

Teraz można zdefiniować kilka Rectangle wartości:

λ> let a = rectangle {len = 1} 

λ> a 
Rectangle {len = 1, width = 0} 
4

nie jest to, czego szukając po prostu tego:

data Rectangle = Point | Line Int | Rectangle Int Int