Làm cách nào tôi có thể sử dụng danh sách có độ dài tối thiểu cố định một cách tổng thể và thanh lịch?


10

Tôi hiện đang xử lý một chức năng như thế này:

foo = (\(a:b:c:d:e:f:_) -> foobar a b c d e f) . (++ repeat def)

Nói cách khác, được đưa ra một danh sách, nó sử dụng sáu yếu tố đầu tiên cho một cái gì đó, và nếu danh sách đó dài hơn sáu yếu tố, nó sẽ sử dụng defnhư một điểm thay thế cho những yếu tố còn thiếu. Đây là tổng số, nhưng các phần của nó không (giống như map fromJust . filter isJust), vì vậy tôi không thích nó. Tôi đã cố gắng viết lại cái này để nó không cần sử dụng bất kỳ bộ phận nào và nhận được cái này:

foo [] = foobar def def def def def def
foo [a] = foobar a def def def def def
foo [a,b] = foobar a b def def def def
foo [a,b,c] = foobar a b c def def def
foo [a,b,c,d] = foobar a b c d def def
foo [a,b,c,d,e] = foobar a b c d e def
foo (a:b:c:d:e:f:_) = foobar a b c d e f

Về mặt kỹ thuật tôi đã làm những gì tôi muốn, nhưng bây giờ đây là một mớ hỗn độn khổng lồ. Làm thế nào tôi có thể làm điều này một cách thanh lịch và ít lặp đi lặp lại?


2
Có lẽ, viết một uncons :: Default a => [a] -> (a,[a])cái mà mặc định def. Hoặc một mặc định takeWithDef. Và / hoặc một mẫu xem / từ đồng nghĩa mẫu. Điều này đòi hỏi phải viết một số mã trợ giúp, mặc dù.
chi

@chi Tôi nghĩ đó là những gì tôi sẽ đi với. Nếu bạn làm cho nó một câu trả lời, tôi sẽ chấp nhận nó.
Joseph Sible-Phục hồi Monica

2
Đối với những gì nó có giá trị, tôi nghĩ rằng đối số tổng thể case xs ++ repeat def of a:b:c:d:e:f:_ -> ...là đủ cục bộ mà tôi sẽ không nghĩ hai lần về việc chỉ sử dụng nó và bỏ qua tất cả các máy móc bổ sung mà các câu trả lời hiện có đang giới thiệu. Nói chung, các đối số tổng thể toàn cầu hơn (liên quan đến các bất biến được duy trì trên nhiều lệnh gọi hàm, ví dụ) khiến tôi lo lắng.
Daniel Wagner

Trên thực tế takeWithDefkhông thể sử dụng được nếu nó trả về một danh sách thông thường, vì chúng ta cần phải khớp mẫu: - / Giải pháp phù hợp là những gì Daniel đã viết dưới đây trong câu trả lời thứ hai của mình. unconschỉ nhận được phần tử đầu tiên, vì vậy nó không hữu ích.
chi

Câu trả lời:


8

Sử dụng gói an toàn , bạn có thể viết, ví dụ:

(!) = atDef def
foo xs = foobar (xs ! 0) (xs ! 1) (xs ! 2) (xs ! 3) (xs ! 4) (xs ! 5)

6

Điều này ít nhất là ngắn hơn:

foo (a:b:c:d:e:f:_) = foobar a b c d e f
foo xs = foo (xs ++ repeat def)

Bạn có thể dễ dàng thấy rằng các mô hình là đầy đủ, nhưng bây giờ bạn phải suy nghĩ một chút để thấy rằng nó luôn luôn chấm dứt. Vì vậy, tôi không biết nếu bạn có thể coi đó là một cải tiến.

Nếu không, chúng ta có thể làm điều đó với đơn nguyên nhà nước, mặc dù nó hơi nặng:

foo = evalState (foobar <$> pop <*> pop <*> pop <*> pop <*> pop <*> pop)
  where
    pop = do xs <- get
             case xs of [] -> pure def
                        y:ys -> put ys >> pure y

Tôi cũng có thể tưởng tượng sử dụng một loại luồng vô hạn như

data S a = S a (S a)

bởi vì sau đó bạn có thể xây dựng foora khỏi repeat :: a -> S a, prepend :: [a] -> S a -> S atake6 :: S a -> (a,a,a,a,a,a), tất cả đều có thể là tổng. Có lẽ không đáng nếu bạn chưa có loại tiện dụng như vậy.


3
Ồ, tôi thích ý tưởng stream rất nhiều. Với một constructor infix như data S a = a :- S a; infixr 5 :-nó trông khá sạch sẽ; foo xs = case prepend xs (repeat def) of a:-b:-c:-d:-e:-f:-_ -> foobar a b c d e f.
Daniel Wagner

4

Chỉ để cho vui (và không được đề xuất, đây là cho các cuộc vui), đây là một cách khác:

import Data.Default

data Cons f a = a :- f a
infixr 5 :-

data Nil a = Nil -- or use Proxy

class TakeDef f where takeDef :: Default a => [a] -> f a
instance TakeDef Nil where takeDef _ = Nil
instance TakeDef f => TakeDef (Cons f) where
    takeDef (x:xs) = x :- takeDef xs
    takeDef xs = def :- takeDef xs

foo xs = case takeDef xs of
    a:-b:-c:-d:-e:-f:-Nil -> foobar a b c d e f

Loại mà bạn sử dụng trong mẫu khớp với số lượng để vượt qua mức tự nhiên cấp loại để takeDefnói có bao nhiêu yếu tố cần xem xét.


1
Đây là cách tiếp cận ưa thích của tôi cho đến nay. Tôi có thể sử dụng một mô hình xem để bổ sung cho nó. (Tại sao "không được đề xuất"? Nhược điểm là gì?)
chi

3
Nó thể hiện chính xác những gì bắt đầu sai khi bạn đầu tư mạnh vào lập trình cấp độ: thế nào là một chương trình một dòng, có thể hiểu được ngay lập tức thành mười dòng yêu cầu người đọc tham gia nghiêm túc vào công cụ suy luận kiểu tinh thần của họ.
Daniel Wagner

1
Tôi thấy điểm của bạn. Tôi tính foo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e flà một dòng. Tôi không tính phần còn lại vì đó là mã nên có trong một số thư viện để sử dụng lại. Nếu nó chỉ được viết cho trường hợp này, thì rõ ràng nó quá mức cần thiết như bạn nói.
chi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.