2012-01-24 36 views
9

Tôi đang cố gắng làm những gì phải rõ ràng trong Haskell, được chuyển từ Just [1]Just [2] thành Just [1, 2]. Tuy nhiên tôi không thể tìm thấy bất cứ điều gì trực tuyến như tôi tiếp tục tìm các trang liên quan nhưng không hữu ích. Vì vậy, làm thế nào để bạn đạt được điều này?Sáp nhập/Gắn thêm Justs vào Haskell

Trả lời

15

Bạn có thể sử dụng liftA2 (++):

liftA2 (++) :: Maybe [a] -> Maybe [a] -> Maybe [a] 

liftA2 chỉ nâng một hàm nhị phân thành một Applicative. Applicative s được thiết kế để nâng các chức năng của các đối số tùy ý trong một ngữ cảnh, vì vậy chúng hoàn hảo cho việc này. Trong trường hợp này, Applicative chúng tôi đang sử dụng là Maybe. Để xem cách làm việc này, chúng ta có thể nhìn vào định nghĩa:

liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c 
liftA2 f a b = f <$> a <*> b 

(<$>) chỉ thang máy bất kỳ chức năng trên các giá trị tinh khiết với một hoạt động bên trong f: (a -> b) -> f a -> f b. (Nó chỉ là một bí danh cho fmap, nếu bạn đã quen thuộc với Functor s.) Đối với Maybe:

_ <$> Nothing = Nothing 
f <$> Just x = Just (f x) 

(<*>) là một chút phức tạp hơn: nó áp dụng một hàm bên trong f đến một giá trị bên trong f: f (a -> b) -> f a -> f b. Đối với Maybe:

Just f <*> Just x = Just (f x) 
_ <*> _ = Nothing 

(Trong thực tế, f <$> x là điều tương tự như pure f <*> x, đó là Just f <*> x cho Maybe.)

Vì vậy, chúng ta có thể mở rộng định nghĩa của liftA2 (++):

liftA2 (++) a b = (++) <$> a <*> b 

-- expand (<$>) 
liftA2 (++) (Just xs) b = Just (xs ++) <*> b 
liftA2 (++) _ _ = Nothing 

-- expand (<*>) 
liftA2 (++) (Just xs) (Just ys) = Just (xs ++ ys) 
liftA2 (++) _ _ = Nothing 

Thật vậy, chúng tôi có thể sử dụng các toán tử này để nâng một hàm số bất kỳ số đối số nào vào bất kỳ Applicative, j nào bằng cách theo mẫu của liftA2. Điều này được gọi là kiểu ứng dụng và rất phổ biến trong mã Haskell thành ngữ. Trong trường hợp này, thậm chí có thể thành ngữ hơn để sử dụng trực tiếp bằng cách viết (++) <$> a <*> b, nếu ab là các biến. (Mặt khác, nếu bạn đang áp dụng nó một phần - giả sử, để chuyển nó cho một hàm bậc cao hơn - thì liftA2 (++) là thích hợp hơn.)

Mỗi MonadApplicative, vì vậy nếu bạn thấy mình đang cố gắng "nâng" hàm thành ngữ cảnh, Applicative có lẽ là những gì bạn đang tìm kiếm.

+0

Tuyệt vời :) Cảm ơn, bạn đã lưu tôi xé tóc ra. Đừng cho rằng bạn biết tương đương với '[2]' và 'Chỉ [3]' -> 'Chỉ cần [2, 3]' phải không? :) –

+4

@DeanBarnes: '(2 :) <$> Chỉ cần [3]' –

+0

Câu trả lời tuyệt vời, cảm ơn bạn @ehird! Đây là cơ bản tham khảo của tôi từ bây giờ :) –

3

trong khi câu trả lời @ ehird là tuyệt vời, tôi đã sử dụng một giải pháp noobish theo hình thức:

mergeJust a b = do 
    a' <- a 
    b' <- b 
    return (a' ++ b') 
+2

+1 ngay cả noobs, được trang bị các công cụ đơn giản, có thể giải quyết vấn đề này. Bạn cũng có thể viết giống như một đơn nguyên hiểu: '[a '++ b' | a '<- a, b' <- b] ' –

3

Để mở rộng giải pháp cho một danh sách các Just s, bạn có thể sử dụng

fmap join $ sequence [Just[1],Just[2],Just[3]] 
-- Just [1,2,3] 
1

Vì nó không được đề cập trong các giải pháp khác, tôi sẽ nói nó ở đây. Cách đơn giản nhất để hoàn thành nhiệm vụ của bạn, theo ý kiến ​​của tôi là sử dụng <> (hoặc mappend) từ Data.Monoid.

import Data.Monoid 

Just [1,2] <> Just [7,8] == Just [1,2,7,8] 

Tuy nhiên, lưu ý rằng giải pháp này không giống như các giá trị Nothing.

Just [1,2] <> Nothing ---> Just [1,2] 
--However 
(++) <$> Just [1,2] <*> Nothing ---> Nothing 
+0

Đôi khi điều này sẽ là hành vi thích hợp, đôi khi không. –

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