2010-05-24 20 views
9

Giả sử tôi có một chức năng với chữ ký loại sau đây:Sử dụng các mục trong một danh sách như các đối số

g :: a -> a -> a -> b 

Tôi cũng có một danh sách các a s-chúng ta hãy gọi nó xs -đó Tôi biết sẽ có ít nhất ba mặt hàng. Tôi muốn áp dụng g cho ba mục đầu tiên của xs. Tôi biết tôi có thể xác định một bộ tổ hợp như sau:

($$$) :: (a -> a -> a -> b) -> [a] -> b 
f $$$ (x:y:z:_) = f x y z 

Sau đó tôi chỉ có thể sử dụng g $$$ xs. Điều này làm cho $$$ một chút như uncurry, nhưng đối với một hàm có ba đối số cùng loại và danh sách thay vì một bộ tuple.

Có cách nào để làm điều này một cách tự nhiên bằng cách sử dụng bộ kết hợp tiêu chuẩn? Hay đúng hơn, cách thành ngữ nhất để làm điều này trong Haskell là gì? Tôi nghĩ rằng cố gắng pointfree trên một phiên bản không infix của $$$ có thể cho tôi một số ý tưởng về nơi để bắt đầu, nhưng đầu ra là một abomination với 10 flip s, một số ít head s và tail s và ap s và 28 dấu ngoặc đơn.

(NB: Tôi biết điều này không phải là một điều tuyệt vời Haskelly để làm ở nơi đầu tiên, nhưng tôi đã đi qua một vài tình huống mà nó có vẻ như một giải pháp hợp lý, đặc biệt là khi sử dụng Parsec. chắc chắn chấp nhận "không bao giờ làm điều này trong mã thực" nếu đó là câu trả lời tốt nhất, nhưng tôi muốn thấy một số thủ thuật thông minh liên quan đến ((->) r) đơn nguyên hoặc bất cứ điều gì.)

+4

Tôi không thấy có gì sai với mã bạn có. Đó là ngắn và cho điểm. Không phải tất cả mọi thứ phải (hoặc nên) pointfree. – sepp2k

+1

Tôi không nhất thiết phải nghĩ rằng có bất cứ điều gì sai trái với nó - Tôi chỉ tò mò về việc liệu có một cách ngắn gọn để làm điều này mà không xác định một tổ hợp mới như '$$$'. –

+0

Lưu ý rằng đây là một phần chức năng, vì vậy ... có lẽ là một ý tưởng tồi nói chung :) Bạn có thể tốt hơn với một '($$$) :: (a -> a -> a -> b) -> [a] -> Có thể b' –

Trả lời

12

Hay đúng hơn, cách thành ngữ nhất để làm điều này trong Haskell là gì?

Thành ngữ? Nếu bạn thực sự muốn một hàm thực hiện những gì ($$$) thực hiện, mã bạn có có thể là thành ngữ như bạn sẽ nhận được.

tôi muốn thấy một số thủ thuật thông minh

Oh, tốt, trong rằng hợp cụ thể.

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
class ListApply f a r | f -> a r where 
    ($...) :: f -> [a] -> r 

instance (TypeCast b r) => ListApply b a r where 
    x $... _ = typeCast x 

instance (ListApply f a r) => ListApply (a -> f) a r where 
    f $... (x:xs) = (f x) $... xs 

Có bạn đi, một giải pháp hoàn toàn chung: Với một hàm của arity tùy ý với một chữ ký như a -> a ... -> b, áp dụng nó vào nhiều yếu tố của một danh sách [a] khi cần thiết.Một cuộc biểu tình:

ones :: [Int] 
ones = repeat 1 

test1 x = x 
test2 x y = x + y 
test3 x y z = (x + z) * (y + z) 

Trong GHCi:

> test1 $... ones 
1 
> test2 $... ones 
2 
> test3 $... ones 
4 

Tôi chắc chắn sẽ chấp nhận "đừng bao giờ làm điều này trong mã thực" nếu đó là câu trả lời tốt nhất

Bạn có lẽ muốn đi với điều đó.


Oh, và một chút soạn sẵn cần thiết để chạy đoạn code trên:

class TypeCast a b | a -> b, b->a where typeCast :: a -> b 
class TypeCast' t a b | t a -> b, t b -> a where typeCast' :: t->a->b 
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b 
instance TypeCast' () a b => TypeCast a b where typeCast x = typeCast'() x 
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast'' 
instance TypeCast''() a a where typeCast'' _ x = x 

Đây là con dao quân đội swiss loại cấp lập trình meta, biếu không của Oleg Kiselyov.

7
f $$$ (x:y:z:_) = f x y z 

Theo tôi đây là cách thành ngữ và ngắn gọn nhất. Nếu số lượng đối số là khác nhau, bạn có thể sử dụng Template Haskell hoặc làm điều đó lặp đi lặp lại - xác định:

zero = const 
next n f (x:xs) = n (f x) xs 

sau đó chức năng của bạn là next (next (next zero))), và các công trình này cho bất kỳ tổ của next.

Ngoài ra bạn có thể phá vỡ nó để combinators nguyên thủy hơn:

firstThree (x:y:z:_) = (x,y,z) 
uncurry3 f (x,y,z) = f x y z 
g f = uncurry3 f . firstThree 
Các vấn đề liên quan