2012-01-31 29 views
5

Là một bài tập, tôi đang thực hiện trong hoạt động 'khuyết điểm' của Haskell tạo thành một cặp từ hai giá trị thuộc bất kỳ loại nào. Thực hiện các kiểu dữ liệu cần thiết là dễ dàng đủ:A 'khuyết điểm' trong Haskell hiển thị giống như đối tượng Scheme

data Nil = Nil deriving (Eq) 
data Pair a b = Cons a b deriving (Eq) 

car (Cons x _) = x 
cdr (Cons _ y) = y 

caar = car . car 
cdar = cdr . car 
cadr = car . cdr 
cddr = cdr . cdr 

*Main> cddr (Cons 55 (Cons (1,2,3,4) "hello, world!")) 
"hello, world!" 
*Main> 

nhưng lấy cảm hứng từ this thread, tôi muốn làm cho các cặp kết quả in ra như danh sách Scheme sẽ - bao gồm cả "danh sách không đúng" khét tiếng (1 2 3 4.). Việc triển khai của tôi (xem bên dưới) đang hoạt động đối với Char's:

*Main> Cons 'a' (Cons 'b' (Cons 'c' Nil)) 
('a' 'b' 'c') 
*Main> Cons 'a' (Cons 'b' 'c') 
('a' 'b' . 'c') 
*Main> Cons (Cons 'a' 'b')(Cons 'c' (Cons 'd' Nil)) 
(('a' . 'b') 'c' 'd') 

Nó không hoạt động tốt cho Int (hoặc bất kỳ loại dữ liệu nào khác). Vì vậy, câu hỏi của tôi là: làm thế nào tôi có thể làm cho công việc này cho các loại dữ liệu khác? ví dụ, tôi muốn nó hoạt động như thế này:

*Main> Cons 5 (Cons "hello" (Cons False Nil)) 
(5 "hello" False) 

đầy đủ thực hiện hiện tại của tôi sau:

data Nil = Nil deriving (Eq) 
data Pair a b = Cons a b deriving (Eq) 

car (Cons x _) = x 
cdr (Cons _ y) = y 

caar = car . car 
cdar = cdr . car 
cadr = car . cdr 
cddr = cdr . cdr 

instance Show Nil where show _ = "()" 

class ShowPair a where 
    showRight::a->String 

instance (Show a, ShowPair a, ShowPair b)=>Show (Pair a b) where 
    show (Cons car cdr) = "(" ++ (show car) ++ (showRight cdr) ++ ")" 

instance (Show a, ShowPair a, ShowPair b)=>ShowPair (Pair a b) where 
    showRight (Cons car cdr) = " " ++ (show car) ++ (showRight cdr) 

instance ShowPair Char where 
    showRight x = " . " ++ show x 

instance ShowPair Int where 
    showRight x = " . " ++ show x 

instance ShowPair Nil where 
    showRight _ = "" 
+1

Haskell đi kèm với một thao tác có sẵn có thể tạo thành một cặp từ hai giá trị thuộc bất kỳ loại nào: '(a, b)'. Nó có thể được đánh vần '(,) a b' nếu bạn muốn có thể sử dụng nó như bạn làm một hàm. '()' sau đó lấy một phần của 'Nil' của bạn. 'car' sau đó được đánh vần' fst', và 'cdr' là' snd'. – Ben

+0

@Ben Hiểu - Tôi biết có khả năng là tôi đang làm một số sáng chế bánh xe ở đây. Có cách nào để có được cặp sản xuất với (,) để in ra như danh sách Đề án? – gcbenison

Trả lời

6

Dưới đây là một lựa chọn. Đầu tiên, kích hoạt các phần mở rộng bằng cách đặt dòng này ở phía trên cùng của tập tin của bạn:

{-# LANGUAGE FlexibleInstances, OverlappingInstances, UndecidableInstances#-} 

Tiếp theo, loại bỏ ShowPair trường hợp của bạn cho CharInt.

Bây giờ thêm một trường hợp ShowPair cho bất cứ điều gì với Show:

instance Show a => ShowPair a where showRight = (" . " ++) . show 

này bây giờ đảm bảo rằng bất kỳ loại a mà là một thể hiện của Show cũng là một thể hiện của ShowPair nơi nó được hiển thị bằng cách thêm vào trước một . để nó dạng chuỗi bình thường. Tuy nhiên, nếu loại có ví dụ cụ thể hơn ShowPair (ví dụ: Nil), Haskell sẽ sử dụng loại đó.

Đây không phải là một phần của Haskell chuẩn, vì vậy bạn cần phải bật ba tiện ích mở rộng ngôn ngữ. Hãy xem How to write an instance for all types in another type class? để biết thêm thông tin về số lý do tại sao bạn cần các tiện ích.

+0

Cảm ơn lời giải thích tuyệt vời này và liên kết đến bài đăng có liên quan! – gcbenison

2

Đưa các nhận xét vào câu hỏi đề cập đến loại cặp gốc, mà tôi sẽ sử dụng trong câu trả lời này. Tôi cũng sẽ thay thế Nil của bạn bằng loại đơn vị Haskell ().

Đây là một chút ngoài những gì bạn đang yêu cầu, nhưng tôi nghĩ nó đáng nói. Rất khó trong Haskell để nắm bắt khái niệm "danh sách" trong Đề án trừ khi bạn "lừa" và sử dụng phần mở rộng như Data.Dynamic. Điều này là do từ quan điểm của "tinh khiết", Haskell không được nối tiếp, rất khó nếu không thể gán tất cả các danh sách Đề án cùng loại. Điều này có nghĩa là trong khi Scheme cho phép bạn viết các hàm có danh sách bất kỳ, đúng hoặc không đúng, bạn sẽ gặp khó khăn khi thực hiện tương tự trong Haskell (và vì lý do chính đáng, danh sách "không phù hợp" có thể không tồn tại).

Vì vậy, ví dụ, về cơ bản bạn đã chọn sử dụng (a, b) làm loại cặp giống như Đề án.Bây giờ giả sử chúng ta có những danh sách Scheme:

(define zero '()) 
(define one '(1)) 
(define two '(1 2)) 
(define three '(1 2 3)) 
(define four '(1 2 3 4)) 

Dưới đây là một bản dịch đơn giản về cặp Haskell, tương ứng với cách bạn đang làm việc đó:

zero ::() 
zero =() 

one :: (Integer,()) 
one = (1,()) 

two :: (Integer, (Integer,())) 
two = (1, (2,())) 

three :: (Integer, (Integer, (Integer,()))) 
three = (1, (2, (3,()))) 

four :: (Integer, (Integer, (Integer, (Integer,())))) 
four = (1, (2, (3, (4,())))) 

Điều quan trọng là trong Đề án bạn có thể dễ dàng viết một chức năng mà chạy trên tất cả các danh sách:

(define (reverse list) 
(foldl cons '() list)) 

(define (filter pred? list) 
    (foldr (lambda (elem rest) 
      (if (pred? elem) 
       (cons elem rest) 
       rest)) 
     '() 
     list)) 

(define (foldl fn init list) 
    (if (null? list) 
     init 
     (foldl fn (fn (car list) init) (cdr list)))) 

(define (foldr fn init list) 
    (if (null? list) 
     init 
     (fn (car list) 
      (foldr fn init (cdr list))))) 

trong dịch Haskell này, bạn không thể làm điều đó một cách dễ dàng chút nào, bởi vì "danh sách" có độ dài khác nhau có typ khác nhau es. Và nó trở nên tệ hơn khi bạn xem xét sự khác biệt giữa reverse (mà phải mất một danh sách dài n và tạo ra một danh sách dài n) và filter (mà phải mất một danh sách dài n và tạo ra một danh sách dài mn sao cho m chỉ có thể được biết khi chạy).

+0

Hmm, vâng - Tôi thấy làm thế nào nó sẽ trở nên khó xử khi cố gắng thực hiện 'filter' và' reverse' trong Haskell cho một "danh sách whatevers" - mặc dù cadr, cddr v.v. khá dễ dàng – gcbenison

Các vấn đề liên quan