ฉันจะใช้รายการความยาวขั้นต่ำคงที่ได้ทั้งหมดและสวยงามได้อย่างไร


10

ฉันกำลังจัดการกับฟังก์ชั่นที่จะเป็นดังนี้:

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

กล่าวอีกนัยหนึ่งเมื่อได้รับรายการจะใช้องค์ประกอบหกรายการแรกสำหรับบางสิ่งและหากรายการมีความยาวน้อยกว่าหกองค์ประกอบรายการนั้นจะใช้defเป็นรายการสแตนด์บายสำหรับรายการที่หายไป นี่คือทั้งหมด แต่ชิ้นส่วนของมันไม่ได้ (เหมือนmap fromJust . filter isJust) ดังนั้นฉันจึงไม่ชอบ ฉันพยายามเขียนใหม่เพื่อที่ว่ามันไม่จำเป็นต้องใช้ความลำเอียงและได้สิ่งนี้:

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

ฉันทำสิ่งที่ฉันต้องการในทางเทคนิคแล้ว แต่ตอนนี้มันเป็นเรื่องใหญ่โต ฉันจะทำสิ่งนี้อย่างสง่างามและซ้ำซากน้อยลงได้อย่างไร


2
บางทีเขียนที่เริ่มต้นที่uncons :: Default a => [a] -> (a,[a]) หรือผิดนัดdef takeWithDefและ / หรือรูปแบบคำพ้อง / รูปแบบการดู แม้ว่าจะต้องมีการเขียนรหัสผู้ช่วยเสริมบ้าง
Chi

@chi ฉันคิดว่านั่นคือสิ่งที่ฉันจะไปด้วย หากคุณตอบคำถามนี้ฉันจะยอมรับมัน
Joseph Sible-Reinstate Monica

2
สำหรับสิ่งที่คุ้มค่าฉันคิดว่าข้อสรุปทั้งหมดสำหรับcase xs ++ repeat def of a:b:c:d:e:f:_ -> ...ท้องถิ่นนั้นเพียงพอที่ฉันจะไม่คิดสองครั้งเกี่ยวกับการใช้มันและข้ามกลไกพิเศษทั้งหมดที่คำตอบที่มีอยู่กำลังเปิดตัว โดยทั่วไปแล้วมันคือข้อโต้แย้งจำนวนทั้งสิ้นทั่วโลก (ซึ่งเกี่ยวข้องกับค่าคงที่ที่เก็บรักษาไว้ในการเรียกใช้ฟังก์ชันหลายครั้ง) ที่ทำให้ฉันรู้สึกประหม่า
Daniel Wagner

อันที่จริงtakeWithDefไม่สามารถใช้งานได้ถ้ามันส่งกลับรายการปกติเนื่องจากเราต้องจัดรูปแบบที่ตรงกับที่: - / ทางออกที่เหมาะสมคือสิ่งที่แดเนียลเขียนไว้ด้านล่างในคำตอบที่สองของเขา unconsรับเฉพาะองค์ประกอบแรกเท่านั้นดังนั้นจึงไม่มีประโยชน์
ไค

คำตอบ:



6

อย่างน้อยสั้นกว่านี้:

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

คุณสามารถเห็นได้อย่างง่ายดายว่ารูปแบบหมดจด แต่ตอนนี้คุณต้องคิดสักหน่อยเพื่อดูว่ามันจะยุติลงเสมอ ดังนั้นฉันไม่รู้ว่าคุณสามารถพิจารณาได้หรือไม่ว่าเป็นการปรับปรุง

มิฉะนั้นเราสามารถทำกับรัฐ monad ได้แม้ว่ามันจะเป็นเฮฟวี่เวทเล็กน้อย:

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

ฉันสามารถจินตนาการโดยใช้ประเภทสตรีมไม่สิ้นสุดเช่น

data S a = S a (S a)

แล้วเพราะคุณสามารถสร้างfooออกจากrepeat :: a -> S a, prepend :: [a] -> S a -> S aและtake6 :: S a -> (a,a,a,a,a,a)ทั้งหมดซึ่งอาจจะรวม อาจไม่คุ้มค่าหากคุณยังไม่มีประเภทที่สะดวก


3
โอ้ฉันชอบกระแสไอเดียมาก ด้วยตัวสร้าง infix เหมือนdata S a = a :- S a; infixr 5 :-มันดูค่อนข้างสะอาด foo xs = case prepend xs (repeat def) of a:-b:-c:-d:-e:-f:-_ -> foobar a b c d e f.
Daniel Wagner

4

เพียงเพื่อความสนุก (และไม่แนะนำนี่คือเพื่อ funsies) นี่เป็นอีกวิธีหนึ่ง:

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

ประเภทที่คุณใช้ในรูปแบบจับคู่กับจำนวนการส่งผ่านประเภทระดับธรรมชาติเพื่อtakeDefบอกจำนวนองค์ประกอบที่ต้องดู


1
นี่คือวิธีที่ฉันชอบ ฉันอาจใช้รูปแบบมุมมองเพื่อเติมเต็ม (? ทำไม "ไม่แนะนำ" อะไรคือข้อเสีย?)
ไค

3
มันบ่งบอกสิ่งที่ผิดพลาดเมื่อคุณลงทุนอย่างมากในการเขียนโปรแกรมระดับประเภท: อะไรคือบอลลูนโปรแกรมหนึ่งบรรทัดที่เข้าใจได้ทันทีในสิบบรรทัดที่ผู้อ่านต้องใช้เครื่องมืออนุมานประเภทจิตอย่างจริงจัง
Daniel Wagner

1
ฉันเห็นประเด็นของคุณ ฉันนับfoo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e fเป็นหนึ่งบรรทัด ฉันไม่นับส่วนที่เหลือเนื่องจากเป็นรหัสที่ควรอยู่ในห้องสมุดบางแห่งเพื่อนำมาใช้ซ้ำ หากจะต้องมีการเขียนเฉพาะสำหรับกรณีนี้มันชัดเจนเกินไปที่คุณพูด
ไค
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.