Vì vậy, cách tốt nhất để hiểu nó là bằng cách làm điều đó. Dưới đây có một triển khai foldlM
sử dụng foldl
thay vì foldr
. Đó là một bài tập hay, hãy thử và đến sau với giải pháp tôi đề xuất. Ví dụ giải thích tất cả lý do tôi đã thực hiện để đạt được nó, có thể khác với lý do của bạn và có thể là sai lệch vì tôi đã biết về việc sử dụng bộ tích lũy hàm.
Bước 1 : Chúng ta hãy thử viết foldlM
theofoldl
-- this doesn't compile because f returning type is (m b) and not just (b)
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f z0 xs
-- So let substitute f by some undefined f'
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' = undefined
-- cool, but f' should use f somehow in order to get the monadic behaviour
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' z0 xs
where f' b a = f somethingIDontkNow
Ở đây bạn nhận ra rằng đó f'
là thuần túy và bạn cần trích xuất kết quả f
để nhập kết quả khớp. Cách duy nhất để "giải nén" một giá trị đơn trị là với >>=
toán tử, nhưng toán tử đó cần được bọc ngay sau khi sử dụng.
Vì vậy, như một kết luận: Mỗi khi bạn kết thúc với việc tôi muốn hoàn toàn mở khóa đơn nguyên này , hãy từ bỏ. Không đúng cách
Bước 2 : Chúng ta hãy thử viết foldlM
theo cách foldl
nhưng trước tiên sử dụng []
như có thể gập lại, vì nó rất dễ để khớp mẫu (nghĩa là chúng ta không thực sự cần sử dụng fold
)
-- This is not very hard. It is pretty standard recursion schema. :)
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
Ok, thật dễ dàng. Hãy so sánh định nghĩa với foldl
định nghĩa thông thường cho danh sách
foldlM' :: (Monad m) => (b -> a -> m b) -> b -> [a] -> m b
foldlM' f z0 [] = return z0
foldlM' f z0 (x:xs) = f z0 x >>= \c -> foldlM' f c xs
myfoldl :: (b -> a -> b) -> b -> [a] -> b
myfoldl f z0 [] = z0
myfoldl f z0 (x:xs) = foldl f (f z0 x) xs
Mát mẻ!! chúng khá giống nhau Các trường hợp tầm thường là về điều tương tự chính xác. Trường hợp đệ quy hơi khác một chút, bạn muốn viết một cái gì đó giống như : foldlM' f (f z0 x) xs
. Nhưng không được biên dịch như trong bước 1, vì vậy bạn có thể nghĩ OK, tôi không muốn áp dụng f
, chỉ để giữ một tính toán như vậy và soạn nó với >>=
. Tôi muốn viết một cái gì đó giống như foldlM' f (f z0 x >>=) xs
nếu nó có ý nghĩa ...
Bước 3 Nhận ra rằng những gì bạn muốn tích lũy là một thành phần chức năng và không phải là kết quả. ( ở đây tôi có lẽ thiên vị bởi thực tế là tôi đã biết nó vì bạn đã đăng nó ).
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' initFunc xs
where initFunc = undefined :: b -> m b
f' = undefined :: (b -> m b) -> a -> (b -> m b) -- This type signature can be deduce because f' should be applied to initFunc and a's from t a.
Theo loại initFunc
và sử dụng kiến thức của chúng tôi từ bước 2 (định nghĩa đệ quy), chúng tôi có thể suy ra điều đó initFunc = return
. Định nghĩa của f'
có thể được hoàn thành biết rằng f'
nên sử dụng f
và >>=
.
foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
foldlM f z0 xs = foldl f' return xs z0
-- ^^^^^^
-- |- Initial value
where f' b a = \bvalue -> b bvalue >>= \bresult -> f bresult a -- this is equivalent to (b >=> \result -> f result a) which captures the sequence behaviour of the implementation
-- ^ ^^^^^^ ^^^^^^^
-- | | |- This is the result of previous computation
-- | |- f' should return a function b -> m b. Any time you have to return a function, start writing a lambda
-- |- This b is the accumulated value and has type b -> m b
-- Following the types you can write this with enough practise
Như bạn có thể thấy, nó không quá khó để làm điều đó. Nó cần thực hành, nhưng tôi không phải là nhà phát triển haskell chuyên nghiệp và tôi có thể tự làm được, đó là vấn đề thực hành