고정 된 최소 길이 목록을 총체적으로 우아하게 사용하려면 어떻게해야합니까?


10

현재 다음과 같은 기능을 처리하고 있습니다.

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

다시 말해, 목록이 주어지면 처음 6 개의 요소를 무언가에 사용하고, 목록의 길이가 6 개 미만인 경우 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일치하는 패턴을 다음과 같이 패턴 화해야하기 때문에 실제로 는 정규 목록을 반환하면 사용할 수 없습니다. 올바른 해결책은 Daniel이 두 번째 답변에서 아래에 쓴 내용입니다. uncons첫 번째 요소 만 가져 오므로 유용하지 않습니다.
chi

답변:


8

안전한 패키지를 사용하여 다음 과 같이 작성할 수 있습니다.

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

6

이것은 적어도 짧습니다 :

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

패턴이 철저하다는 것을 쉽게 알 수 있지만 이제는 항상 종료되는 것을 볼 수 있습니다. 그래서 당신이 그것을 개선이라고 생각할 수 있는지 모르겠습니다.

그렇지 않으면 우리는 상태 모나드로 할 수 있지만 약간 헤비급입니다.

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
오, 나는 스트림 아이디어를 많이 좋아한다. 인 픽스 생성자와 같이 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
이것은 지금까지 내가 선호하는 접근법입니다. 아마 보완하기 위해 뷰 패턴을 사용했을 것입니다. ( "권장되지 않은"이유는 무엇입니까?)
chi

3
타입 레벨 프로그래밍에 많은 투자를 할 때 잘못되는 일을 정확히 구현합니다. 즉, 한 줄의 즉각적으로 이해할 수있는 프로그램이 10 줄로 확장되어 독자가 정신적 유형 추론 엔진에 진지하게 참여해야합니다.
Daniel Wagner

1
너의 의도를 알 겠어. foo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e f한 줄로 계산 합니다. 나머지 라이브러리는 재사용을 위해 일부 라이브러리에 있어야하는 코드이므로 계산하지 않습니다. 이 경우에만 작성 해야하는 경우 분명히 말하면 과잉입니다.
chi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.