2011-09-27 24 views

Trả lời

13

Có, bạn có thể sử dụng tiện ích mở rộng ViewPatterns.

Prelude> :set -XViewPatterns 
Prelude> let f (last -> x) = x*2 
Prelude> f [1, 2, 3] 
6 

Lưu ý rằng mô hình này sẽ luôn thành công, tuy nhiên, rất có thể bạn sẽ muốn thêm một mô hình cho trường hợp danh sách là rỗng, khác last sẽ ném một ngoại lệ.

Prelude> f [] 
*** Exception: Prelude.last: empty list 

Cũng lưu ý rằng đây chỉ là cú pháp. Không giống như đối sánh mẫu bình thường, đây là O (n), vì bạn vẫn đang truy cập phần tử cuối cùng của danh sách được liên kết đơn lẻ. Nếu bạn cần truy cập hiệu quả hơn, hãy xem xét sử dụng cấu trúc dữ liệu khác như Data.Sequence, cung cấp quyền truy cập O (1) cho cả hai đầu.

+0

cuối cùng ném ngoại lệ vào danh sách trống. Tôi đang tìm cách để tránh xa cuối cùng, đầu, fst, snd. Các hàm không xử lý tốt danh sách trống. Ví dụ, ví dụ, cho phép (x: xs) = "abcdefg" là những gì tôi đang đề cập đến trong câu hỏi của mình. Đã hy vọng có một cách tiếp cận tương tự để có được phần tử cuối cùng, mà không sử dụng các hàm prelude không an toàn. –

+2

@MichaelLitchard: Ví dụ, bạn có thể xác định hàm 'safeLast' trả về' Có thể', sau đó bạn có thể khớp mẫu hoặc có thể đảo ngược danh sách và sau đó sử dụng đối sánh mẫu bình thường. – hammar

+2

vì nó luôn thành công, nó không phải là một "trận đấu mẫu" chút nào. 'f = (* 2). cuối cùng là tốt hơn. – u0b34a0f6ae

11

Bạn có thể sử dụng ViewPatterns để làm mô hình kết hợp vào cuối danh sách, vì vậy chúng ta hãy làm

{-# LANGUAGE ViewPatterns #-} 

và sử dụng reverse như viewFunction, bởi vì nó luôn luôn thành công, ví dụ như vậy

printLast :: Show a => IO() 
printLast (reverse -> (x:_)) = print x 
printLast _ = putStrLn "Sorry, there wasn't a last element to print." 

Điều này là an toàn theo nghĩa là nó không ném bất kỳ ngoại lệ nào miễn là bạn bao trả tất cả các khả năng. (Bạn có thể viết lại nó để trả lại một Maybe, ví dụ.)

Cú pháp

mainFunction (viewFunction -> pattern) = resultExpression

là cú pháp đường cho

mainFunction x = case viewFunction x of pattern -> resultExpression

để bạn có thể nhìn thấy nó thực sự chỉ đảo ngược danh sách rồi mẫu khớp với, nhưng nó đẹp hơn. viewFunction chỉ là bất kỳ chức năng nào bạn thích. (Một trong những mục tiêu của phần mở rộng là để cho phép mọi người sạch sẽ và dễ dàng sử dụng các chức năng accessor cho phù hợp với mô hình để họ không cần phải sử dụng cấu trúc cơ bản của kiểu dữ liệu của họ khi quy định chức năng trên đó.)

4

Các câu trả lời khác giải thích các giải pháp dựa trên ViewPatterns. Nếu bạn muốn làm cho nó nhiều mô hình kết hợp giống như, bạn có thể gói đó vào một PatternSynonym:

tailLast :: [a] -> Maybe ([a], a) 
tailLast [email protected](_:_) = Just (init xs, last xs) 
tailLast _ = Nothing 

pattern Split x1 xs xn = x1 : (tailLast -> Just (xs, xn)) 

và sau đó viết chức năng của bạn như ví dụ

foo :: [a] -> (a, [a], a) 
foo (Split head mid last) = (head, mid, last) 
foo _ = error "foo: empty list" 
2

Đây là ngày đầu tiên lập trình Haskell và tôi cũng gặp phải vấn đề tương tự nhưng tôi không thể giải quyết sử dụng một số loại tạo tác bên ngoài như được đề xuất trong các giải pháp trước.

Cảm giác của tôi về Haskell là nếu ngôn ngữ cốt lõi không có giải pháp cho vấn đề của bạn, thì giải pháp là chuyển đổi vấn đề của bạn cho đến khi nó hoạt động cho ngôn ngữ.

Trong trường hợp này việc chuyển đổi vấn đề có nghĩa là chuyển đổi vấn đề đuôi thành vấn đề đầu, có vẻ như thao tác được hỗ trợ duy nhất trong khớp mẫu. Bạn có thể dễ dàng làm điều đó bằng cách sử dụng danh sách đảo ngược, sau đó làm việc trên danh sách được đảo ngược bằng cách sử dụng phần tử đầu như bạn sử dụng phần tử đuôi trong danh sách gốc và cuối cùng, nếu cần, hoàn nguyên kết quả về thứ tự ban đầu (ví dụ: nếu đó là một danh sách). Ví dụ, được đưa ra một danh sách các số nguyên (ví dụ: [1,2,3,4,5,6]), giả sử chúng ta muốn xây dựng danh sách này trong đó mỗi phần tử thứ hai của danh sách ban đầu bắt đầu từ phần cuối được thay thế bằng đôi của nó (tập thể dục lấy từ Homework1 của this excellent introduction to Haskell): [2,2,6,4,10,6].

Sau đó, chúng ta có thể sử dụng như sau:

revert :: [Integer] -> [Integer] 
revert []  = [] 
revert (x:[]) = [x] 
revert (x:xs) = (revert xs) ++ [x] 

doubleSecond :: [Integer] -> [Integer] 
doubleSecond []  = [] 
doubleSecond (x:[]) = [x] 
doubleSecond (x:y:xs) = (x:2*y : (doubleSecond xs)) 

doubleBeforeLast :: [Integer] -> [Integer] 
doubleBeforeLast l = (revert (doubleSecond (revert l))) 

main = putStrLn (show (doubleBeforeLast [1,2,3,4,5,6,7,8,9])) 

Đó rõ ràng là lâu hơn so với các giải pháp trước đó, nhưng nó cảm thấy hơn Haskell-ish với tôi.

+1

Chào mừng bạn đến với StackOverflow và chúc mừng cho một khởi đầu tốt đẹp như vậy! Đảo ngược danh sách để truy cập vào cuối xa thực sự là một giải pháp rất Haskellish (mặc dù nó khá kém hiệu quả, nhưng việc sử dụng 'last' có thể tồi tệ hơn nếu bạn cần làm nhiều lần ... nếu hiệu suất là rất quan trọng và bạn cần truy cập một phần tử cuối cùng, thì danh sách không phải là một loại phù hợp). – leftaroundabout

+0

Cảm ơn. Ở cấp độ tân binh của tôi, tôi thực sự quan tâm hơn đến việc khám phá các thành ngữ lập trình Haskell, hơn là tìm hiểu về tác động hiệu suất của chúng. Tôi đồng ý nó chắc chắn là một trường hợp tồi tệ hơn để cố gắng sử dụng một danh sách đơn giản liên kết theo thứ tự ngược lại và tôi đoán đây thực sự là lý do tại sao Haskell không cung cấp phù hợp với mô hình đuôi lúc đầu tiên. (Danh sách Haskell được thực hiện bằng cách sử dụng danh sách được liên kết đơn giản, đúng không?) – OlivierD

+1

Đúng. Các danh sách Haskell rất tuyệt vời như các ngăn xếp, hoặc là các luồng vô tận, nhưng chúng thực sự tồi tệ khi truy cập ngẫu nhiên và việc truy cập vào đầu cuối không tốt hơn là truy cập bất kỳ phần tử nào ở giữa. – leftaroundabout

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