하스켈에서의 골프 팁


73

하스켈 골프에 대한 일반적인 팁은 무엇입니까? 나는 Haskell에게 다소 특정한 코드 골프 문제에 적용될 수있는 아이디어를 찾고 있습니다. 답변 당 하나의 팁만 게시하십시오.


Haskell에서 골프를 처음 사용하는 경우 Haskell의 골프 규칙 안내서를 참조하십시오 . Haskell 전용 채팅방이 있습니다 : Of Monads and Men .


1
지금까지 답변 수를 보면 Haskell이 코드 골프에 적합한 언어인지 아닌지 의심됩니다.
Animesh 'The Coder'1

10
답변 당 팁이 하나만있는 이유는 무엇입니까? 또한 모든 언어는 골프를하기에 좋은 언어입니다. 항상 이길 것이라고 기대하지는 마십시오.
unclemeat

6
@unclemeat이 방법으로 사람들은 같은 사람이 같은 답변으로 작성했기 때문에 나쁜 사람을지지하지 않고 좋은 사람을 맨 위로 올릴 수 있습니다.
MasterMastic 2016 년

3
특별 요청, 문자열 압축.
J 앳킨

이것은 아마도 anwer로 적합하지는 않지만 여기에 추가하고 싶습니다 : wiki.haskell.org/Prime_numbers_miscellaneous#One-liners
flawr

답변:


45

이진 함수 대신 접두사 연산자 정의

이는 일반적으로 정의 또는 호출 당 하나 또는 두 개의 공간을 절약합니다.

0!(y:_)=y
x!(y:z)=(x-1)!z

vs.

f 0(y:_)=y
f x(y:z)=f(x-1)z

1 바이트 사업자에 사용할 수있는 기호는 !, #, %, &,와 ?. 다른 모든 ASCII 문장 부호는 Prelude에 의해 연산자로 이미 정의되어 $있거나 (예 :) Haskell의 구문에 특별한 의미가 있습니다 (예 :) @.

5 개 이상의 연산자가 필요한 경우 위와 같은 위의 조합 !#또는 다음과 같은 특정 유니 코드 문장 부호 문자 (UTF-8의 2 바이트)를 사용할 수 있습니다.

¡ ¢ £ ¤ ¥ ¦ § ¨ © ¬ ® ¯ ° ± ´ ¶ · ¸ ¿ × ÷

11
참고 : 세 개 이상의 인수가있는 함수에도 사용할 수 있습니다. (x!y)z=x+y*z그리고 (x#y)z u=x*z+y*u둘 다 예상대로 작동합니다.
Zgarb

3
이것은 또한 함수 인수, 예를 들어 사용할 수 있습니다 \f g(!)x y->f g!x y대신\f g j x y->j(f g)(x y)
Esolanging 과일

2
때때로 당신이 그렇지 않으면 괄호를 사용해야 할 것이다 경우 이항 연산자에 단항 기능을 변경하는 유익한입니다 - g x=…;g(f x)보다 긴_?x=…;0!f x
Angs

29

적절한 경우 무의미한 (또는 자유) 표기법 사용

하나 또는 두 개의 매개 변수가있는 함수는 종종 포인트없이 쓸 수 있습니다.

따라서 요소가 교체 된 튜플 목록에 대한 조회는 순진하게 다음과 같이 작성됩니다.

revlookup :: Eq b => b -> [(a, b)] -> Maybe a
revlookup e l=lookup e(map swap l)

(이 유형은 수행중인 작업을 이해하는 데 도움이됩니다.)

우리의 목적을 위해 이것은 훨씬 낫습니다.

revlookup=(.map swap).lookup

28

목록 모나드 사용

빠른 검토 :

xs >> ys        =  concat $ replicate (length xs) ys
xs >>= f        =  concatMap f xs
mapM id[a,b,c]  =  cartesian product of lists: a × b × c
mapM f[a,b,c]   =  cartesian product of lists: f a × f b × f c

예 :

  • 목록을 두 번 반복

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • 더 짧은 concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • 더 짧은 concat+ 목록 이해

    Prelude> do x<-[1..5];[1..x]
    [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]
    
  • 카티 전 곱

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • 격자의 좌표 목록

    Prelude> mapM(\x->[0..x])[3,2]
    [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2],[3,0],[3,1],[3,2]]
    

1
내가 유용하다고 생각한 또 다른 용도는 [0..b]>>[a]입니다 replicate a b.
밀 마법사

3
@WheatWizard a<$[1..b]는 더 짧습니다 replicate.
Lynn

를 사용 =<<하면 가져옵니다 Control.Monad. 다른 이유로 필요하지 않은 경우 인수를 바꾸고 사용하는 >>=것이 더 간결 해 보입니다.
dfeuer

OTOH, Data.Traversable어쨌든 필요한 경우 직교 곱 예제를로 줄일 수 있습니다 for["Hh","io",".!"]id.
dfeuer

2
(=<<)실제로 Prelude 에 있습니다! 나는 그것을 많이 사용했습니다.
Lynn

28

조건부가 아닌 경비원을 사용하십시오.

f a=if a>0 then 3 else 7
g a|a>0=3|True=7

들여 쓰기가 아닌 세미콜론 사용

f a=do
  this
  that
g a=do this;that

부울 함수에 부울 표현식 사용

f a=if zzz then True else f yyy
g a=zzz||f yyy

(SO는 이것을 별도로 게시하는 데 어려움을 겪고 있습니다)


2
또한 &&목록 이해 안에있을 때 대신 여러 보호대를 사용 하십시오.
John Dvorak

좋은 1 월-당신은 대답으로 만들어야합니다, 내가 투표하겠습니다
bazzargh

5
첫 번째 예에 의해 더욱 단축 할 수 True=>1>0
존 드보락

1
첫 번째 예에서, 당신이 의미하는 것으로 가정합니다f a=if a>0 then 3 else 7
Cyoce

가드가 없으면 가드는 작동합니다.
Akangka

24

interact :: (String → String) → IO ()

사람들은 종종이 기능이 존재한다는 것을 잊어 버립니다. 모든 stdin을 가져 와서 (순수) 기능에 적용합니다. 나는 종종 main줄을 따라 -code를 본다.

main=getContents>>=print.foo

동안

main=interact$show.foo

꽤 짧습니다. Prelude에 있으므로 수입 할 필요가 없습니다!


24

GHC 7.10 사용

이 내용을 담은 GHC의 첫 번째 버전은 2015 년 3 월 27 일 에 발표되었습니다 .

최신 버전이며 Prelude는 골프에 유용한 새로운 기능을 추가했습니다.

(<$>)(<*>)운영

이 유용한 연산자 Data.Applicative는 그것을 만들었습니다! <$>그냥 fmap당신이 교체 할 수 있도록, map f x그리고 fmap f x함께 f<$>x사방 바이트를 다시 승리. 또한 목록 <*>Applicative인스턴스 에서 유용 합니다.

Prelude> (,)<$>[1..2]<*>"abcd"
[(1,'a'),(1,'b'),(1,'c'),(1,'d'),(2,'a'),(2,'b'),(2,'c'),(2,'d')]

(<$)연산자

x<$a동등하다 fmap (const x) a; 즉, 컨테이너의 모든 요소를로 바꿉니다 x.

이것 보다 좋은 대안이 종종있다 replicate: 4<$[1..n]보다 짧다 replicate n 4.

접이식 / 이용 가능한 제안

다음 기능은 목록 작업에서 [a]일반 Foldable유형 으로 해제되었습니다 t a.

fold*, null, length, elem, maximum, minimum, sum, product
and, or, any, all, concat, concatMap

즉, 이제 Maybe a"최대 하나의 요소가있는 목록"처럼 동작하는 작업도 수행합니다 . 예를 들어, null Nothing == True또는 sum (Just 3) == 3. 마찬가지로 값으로 length0을 Nothing, Just값으로 1을 반환 합니다. 쓰는 대신 쓸 x==Just y수 있습니다 elem y x.

튜플에도 적용 할 수 있으며, \(a, b) -> [b]처음 호출 한 것처럼 작동합니다 . 그것은 거의 완전히 쓸모하지만 or :: (a, Bool) -> Bool이상의 문자 짧은 snd, 그리고 elem b보다 짧다 (==b).snd.

모노 이드 기능 memptymappend

종종 생명의 은인은 아니지만 유형을 추론 할 수 있다면 mempty1 바이트보다 짧습니다 Nothing.


5
+1 '<$>`에 대해 듣고 <*>Prelude로 만드는 것이 좋습니다 ! 코드 골프가 아닌 경우에도 유용합니다 (응용 프로그램은 그렇게 긴 단어입니다).
ankh-morpork

플랫 교체에 대한 경고 : 언어 버전이 문제보다 최신 인 경우 솔루션에서 이길 수 없습니다. 기존 답변 또는 답변을 업데이트하려면 기존 솔루션을 덮어 쓰지 마십시오. 부록을 작성하십시오.
John Dvorak

4
거기에 재미있다 [1..2]. 그건 그냥[1,2]
자랑스런 Haskeller

2
같은 버전에서 우리는 또한 가지고 <*에서 Applicative목록에 대한 인 xs <* ys == concatMap (replicate (length ys)) xs. 이것은 다른 xs >> ysxs *> ys있는 것입니다 concat (replicate (length ys)) xs. 이 시점에서도 pure더 짧은 것이 었 return습니다.
Angs

2
이제 ( <>대신 mappendGHC 8.4.1 사용)의 일부를 사용할 수 있습니다 Prelude.
ბიმო

22

사용 1<2대신 True하고 1>2대신 False.

g x|x<10=10|True=x
f x|x<10=10|1<2=x

3
이것은 실제로 Haskell에만 해당되는 것은 아닙니다. 다른 유형의 진실되고 잘못된 값과 달리 부울 유형을 가진 거의 모든 언어에 적용 할 수 있습니다.
피터 테일러

누구든지 이것을 설명 할 수 있습니까?
MasterMastic 2016 년

2
이것은 좋은 예가 아닙니다 f=max 10.
자랑스런 Haskeller

@MasterMastic 이것은 if(true)다른 언어로 작성 되었습니다. 전주곡에서 그렇지 않으면 실제로는 부울 값 True입니다.
자랑스런 Haskeller

2
@PeterTaylor 나는 처음 사용하는 법을 배운 것처럼 나 같은 새로운 해골 인에게 여전히 가치가 있다고 생각합니다 otherwise.
flawr

20

목록 이해력 활용 (영리한 방법으로)

누구나 자신이 유용한 구문이며, 종종 map람다 보다 짧은 것을 알고 있습니다 .

Prelude> [[1..x]>>show x|x<-[1..9]]
["1","22","333","4444","55555","666666","7777777","88888888","999999999"]

또는 filter(선택적으로 map동시에) :

Prelude> [show x|x<-[1..60],mod 60x<1]
["1","2","3","4","5","6","10","12","15","20","30","60"]

그러나 몇 가지 이상한 용도가 있습니다. 예를 들어, 목록 이해에는 <-화살표 가 전혀 필요하지 않습니다 .

Prelude> [1|False]
[]
Prelude> [1|True]
[1]

대신에을 if p then[x]else[]쓸 수 있습니다 [x|p]. 또한 조건을 만족하는 목록의 요소 수를 계산하려면 다음과 같이 직관적으로 작성하십시오.

length$filter p x

그러나 이것은 더 짧습니다.

sum[1|y<-x,p y]

나는 실제로 이것들을 모두 전에 사용했지만 여기에 넣을 생각은 없었습니다.
자랑스런 Haskeller

18

당신을 알고 Prelude

GHCi를 시작하고 Prelude 문서를 스크롤하십시오 . 짧은 이름을 가진 함수를 교차 할 때마다 유용 할 수있는 경우를 찾기 위해 돈을 지불 할 수 있습니다.

예를 들어 문자열 s = "abc\ndef\nghi"을 공백으로 구분 된 문자열 로 변환한다고 가정합니다 "abc def ghi". 확실한 방법은 다음과 같습니다.

unwords$lines s

그러나 당신은 당신이 남용하면 더 잘 할 수있는 max, 그리고 그 사실을 \n < space < printable ASCII:

max ' '<$>s

또 다른 예는 lex :: String -> [(String, String)]매우 신비한 일입니다.

Prelude> lex "   some string of Haskell tokens  123  "
[("some"," string of Haskell tokens  123  ")]

시도 fst=<<lex s공백을 건너 뛰는, 문자열에서 첫 번째 토큰을 얻을 수 있습니다. 여기서 사용하여 henkma 영리한 해결책 lex.showRational값.


16

상수 값 일치

리스트 이해는 상수에서 패턴 일치를 할 수 있습니다.


[0|0<-l]

이것은리스트의 0을 추출합니다 l. 즉,의 수만큼 0의리스트를 만듭니다 l.


[1|[]<-f<$>l] 

이것은 많은 목록을 만드는 1요소가 있기 때문에 's의 lf(사용하여 빈리스트에 소요 <$>중위로 map). sum이러한 요소를 세려면 적용하십시오 .

비교:

[1|[]<-f<$>l]
[1|x<-l,f x==[]]

[x|(0,x)<-l]

상수는 패턴 일치의 일부로 사용될 수 있습니다. 첫 번째 항목이 인 모든 튜플의 두 번째 항목을 추출합니다 0.


이들 모두 변수의 값이 아닌 실제 상수 리터럴이 필요합니다. 예를 들어 외부 바인딩을 덮어 쓰므로 , not let x=1 in [1|x<-[1,2,3]]이 출력됩니다 .[1,1,1][1]x


14

words긴 문자열 목록 대신 사용하십시오 . 이것은 실제로 Haskell에만 국한된 것이 아니며 다른 언어도 비슷한 속임수를 가지고 있습니다.

["foo","bar"]
words"foo bar"  -- 1 byte longer
["foo","bar","baz"]
words"foo bar baz"  -- 1 byte shorter
["foo","bar","baz","qux"]
words"foo bar baz qux"    -- 3 bytes shorter

14

당신의 수도원 기능을 알고

1)를
사용하여 모나 딕 함수를 시뮬레이션합니다 mapM.

코드는 여러 번 가질 sequence(map f xs)수 있지만로 대체 될 수 있습니다 mapM f xs. sequence혼자 사용하는 경우에도 길어집니다 mapM id.

2)
이용하여 기능을 결합 (>>=)(또는 (=<<))

함수 모나드 버전 (>>=)은 다음과 같이 정의됩니다.

(f >>= g) x = g (f x) x 

파이프 라인으로 표현할 수없는 함수를 만드는 데 유용 할 수 있습니다. 예를 들어, \x->x==nub x보다 긴 nub>>=(==), 그리고 \t->zip(tail t)t이상이다 tail>>=zip.


+1-함수에 대한 모나드 인스턴스가 있다는 것을조차 알지 못했습니다. 그것은 매우 편리 할 수 ​​있습니다 :)
Jules

2
사이드 노트 : 그것은의 일부하지만 Applicative하지 Monad의 구현있어 pure보다 짧은뿐만 아니라이 const실제로 이전에 저를 도왔다.
ბიმო

14

인수는 정의보다 짧을 수 있습니다

나는 방금 henkma에 의해 매우 호기심 많은 방법으로 outgolfed했습니다 .

f응답 의 보조 함수 가 응답의 다른 곳에서 사용되지 않고 f한 번 호출 된 연산자를 사용하는 경우 연산자를 인수로 지정하십시오 f.

이:

(!)=take
f a=5!a++3!a
reverse.f

이보다 2 바이트 더 깁니다.

f(!)a=5!a++3!a
reverse.f take

12

단점 연산자 (:)를 사용하십시오.

목록을 연결할 때 첫 번째 길이가 1이면 :대신 사용하십시오.

a++" "++b
a++' ':b  -- one character shorter

[3]++l
3:l    -- three characters shorter

4
올바로 연관되어 있다는 점에 주목할 필요가 있으므로 목록의 시작 부분에서 여러 개의 단일 항목에 계속 사용할 수 있습니다 (예 1:2:3:x:) [1,2,3]++x.
Jules

11

백틱을 너무 자주 사용하지 마십시오. 백틱은 접두사 기능의 섹션을 만들기위한 유용한 도구이지만 잘못 사용되기도합니다.

누군가이 하위 표현식을 작성하는 것을 보았습니다.

(x`v`)

그것은 단지와 동일하지만 v x.

또 다른 예는 (x+1)`div`y 와는 반대로 글을 쓰는 것 div(x+1)y입니다.

나는 주위에 일을 참조 div하고 elem이러한 기능은 일반적으로 일반 코드에서 중위로 사용되기 때문에 더 자주.


접두사 함수 섹션을 만드는 것을 의미하지 않습니까?
Cyoce

@Cyoce 네, 물론
자랑스런 Haskeller

11

패턴 가드 사용

let정의하는 함수의 인수를 해체하는 람다 또는 람다 보다 짧습니다 . 당신이 뭔가해야 할 때 도움이 fromJust에서를 Data.Maybe:

f x=let Just c=… in c

보다 길다

f x=(\(Just c)->c)$…

보다 길다

m(Just c)=c;f x=m$…

보다 길다

f x|Just c<-…=c

실제로, 그것들은 해체하는 대신 평범한 오래된 값을 바인딩 할 때도 짧습니다 : xnor 's tip 참조하십시오 .


음, 람다 하나는 달러 기호를 필요로하지 않으며,이 변경이의 길이와 다음 조각 같은 만드는 것
자랑 haskeller

1
필자 e는 실제로 하나의 토큰이 아니라 $이전 에 필요한 더 긴 표현식 이라고 가정 합니다.이 경우 일반적입니다.
Lynn

11

더 짧은 조건부

last$x:[y|b]

에 해당

if b then y else x

작동 방식은 다음과 같습니다.

             [y|b]   x:[y|b]   last$x:[y|b]
if...      +--------------------------------
b == False | []      [x]       x
b == True  | [y]     [x,y]     y

그럴까요 if b then y else x?
Akangka

@ChristianIrwan 잘 잡았습니다.
xnor

bool목록 이해가 필요하지 않으므로 더 짧게 사용 하지 않겠
습니까

@ Potato44 Data.Bool에 있으며 가져 오는 데 바이트가 필요합니다.
xnor

11

빼기 기호 작업

빼기 부호 -는 많은 구문 규칙에서 성가신 예외입니다. 이 팁에는 Haskell에서 부정과 뺄셈을 표현하는 몇 가지 간단한 방법이 나와 있습니다. 내가 놓친 부분이 있으면 알려주십시오.

부정

  • 식을 부정하려면 e그냥하세요 -e. 예를 들어, -length[1,2]gives -2.
  • 경우 e도 적당히 복잡, 당신은 주위에 괄호가 필요합니다 e,하지만 당신은 일반적으로 주위를 이동하여 바이트를 저장할 수 있습니다 -length(take 3 x)보다 짧습니다 -(length$take 3 x).
  • 경우 e앞에는 =의 중위 연산자 또는 정착 미만 6 이상, 당신은 공간이 필요 : f= -2정의 fk< -2테스트를하는 경우 k보다 작다 -2. 고 정성이 6 이상이면 parens : 2^^(-2)gives 가 필요 합니다 0.25. 당신은 일반적으로 이러한 없애 물건을 다시 정렬 할 수 있습니다 : 예를 들어, 할 -k>2대신 k< -2.
  • 마찬가지로, !연산자 인 경우 고 정성이 최대 6 인 -a!b것처럼 구문 분석되고 (따라서 제공 ) 그렇지 않으면 (그렇게 제공합니다 ). 사용자 정의 연산자와 역틱 된 함수의 기본 고정도는 9이므로 두 번째 규칙을 따릅니다.(-a)!b!-1<1True-(a!b)-[1,2]!!0-1
  • 부정을 함수로 바꾸려면 ( map등과 함께 사용하기 위해) 섹션을 사용하십시오 (0-).

빼기

  • 빼는 함수를 얻으려면 추가 k하는 섹션을 사용하십시오 . 꽤 복잡한 표현 일 수도 있습니다 : 예상대로 작동합니다.(-k+)-kk(-2*length x+)
  • 1을 빼려면 pred양쪽에 공백이 필요하지 않으면 대신 사용하십시오 . 이것은 드문 일반적으로 발생 until하기 때문에, 또는 사용자 정의 함수 map pred x로 대체 될 수 pred<$>xiterate pred x[x,x-1..]. 그리고 f pred x어딘가에 있다면 f어쨌든 infix 함수로 정의해야 합니다. 이 팁을 참조하십시오 .

11

함수 정의 및 / 또는 인수 재정렬

함수 정의에서 패턴 일치 사례의 순서를 변경하여 몇 바이트를 저장할 수도 있습니다. 이러한 절감 효과는 저렴하지만 간과하기 쉽습니다.

예를 들어, 이 답변 의 다음 이전 버전 (일부)을 고려하십시오 .

(g?x)[]=x
(g?x)(a:b)=g(g?x$b)a

이는 ?기본 사례가 빈 목록 인의 재귀 적 정의입니다 . 때문에 []유용한 값이 아닌, 우리는 정의를 교환하고 와일드 카드로 교체해야 _하거나 더미 인수 y바이트를 저장 :

(g?x)(a:b)=g(g?x$b)a
(g?x)y=x

같은 대답에서 다음 정의를 고려하십시오.

f#[]=[]
f#(a:b)=f a:f#b

빈 목록은 반환 값에 발생하므로 사례를 교체하여 2 바이트를 절약 할 수 있습니다.

f#(a:b)=f a:f#b
f#x=x

또한 함수 인수의 순서는 불필요한 공백을 제거하여 때때로 차이를 만들 수 있습니다. 이 답변 의 이전 버전을 고려하십시오 .

h p q a|a>z=0:h p(q+2)(a-1%q)|1<2=1:h(p+2)q(a+1%p)

첫 번째 분기 사이 hp첫 분기 사이에 성가신 공백이 있습니다. h a p q대신 다음을 정의하여 제거 할 수 있습니다 h p q a.

h a p q|a>z=0:h(a-1%q)p(q+2)|1<2=1:h(a+1%p)(p+2)q

10

"그렇지 않은"경비를 낭비하지 마십시오

포괄의 최종 가드 True(짧은 등은 1>0) 변수를 결합 할 수 있습니다. 비교:

... |1>0=1/(x+y)
... |z<-x+y=1/z

... |1>0=sum l-sum m
... |s<-sum=s l-s m

경비원은 필수적이며 그렇지 않으면 낭비되기 때문에 이것을 가치있게하는 데 필요한 것은 거의 없습니다. 한 쌍의 Parens를 저장하거나 두 번 사용되는 길이 3 표현식을 바인딩하는 것으로 충분합니다. 때로는 마지막 사례를 바인딩을 가장 잘 사용하는 표현으로 만들기 위해 경비원을 부정 할 수 있습니다.


10

경비원 ,대신 사용&&

가드의 여러 조건을 모두 ,대신에 결합 할 수 있습니다 &&.

f a b | a/=5 && b/=7 = ...
f a b | a/=5 ,  b/=7 = ...

2
조건이 아니어도 다음과 같은 작업을 수행 할 수 있습니다.f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap

10

지역 선언을위한 더 짧은 구문

때로는 로컬 함수 또는 연산자를 정의해야하지만 추가 인수를 추가 하여 작성 where하거나 let…in최상위 레벨로 올리 려면 많은 바이트가 필요합니다.

g~(a:b)=2!g b where k!l=k:take(a-1)l++(k+1)!drop(a-1)l
g~(a:b)=let k!l=k:take(a-1)l++(k+1)!drop(a-1)l in 2!g b
g~(a:b)=2!g b$a;(k!l)a=k:take(a-1)l++((k+1)!drop(a-1)l)a

다행스럽게도 Haskell은 혼란스럽고 거의 사용되지 않았지만 로컬 선언에 대한 합리적으로 간결한 구문을 가지고 있습니다 .

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

이 경우 :

g~(a:b)|let k!l=k:take(a-1)l++(k+1)!drop(a-1)l=2!g b

이 구문을 다중 문 선언 또는 여러 선언과 함께 사용할 수 있으며 중첩도 가능합니다.

fun1 pattern1 | let fun2 pattern2 = expr2; fun2 pattern2' = expr2' = expr1
fun1 pattern1 | let fun2 pattern2 = expr2; fun3 pattern3 = expr3 = expr1
fun1 pattern1 | let fun2 pattern2 | let fun3 pattern3 = expr3 = expr2 = expr1

변수 또는 다른 패턴을 바인딩하는 데에도 작동하지만 함수를 바인딩하지 않으면 패턴 가드 가 짧아지는 경향이 있습니다.


3
이것은 또한 목록 이해 내에서 작동합니다 [f 1|let f x=x+1].
Laikoni

10

기피 repeat n

-- 8 bytes, whitespace might be needed before and after
repeat n

-- 8 bytes, whitespace might be needed before
cycle[n]

-- 7 bytes, whitespace might be needed before and after, can be reused,
-- needs an assignment, n needs to be global
l=n:l;l

-- 7 bytes, never needs whitespace, n needs to derive from Enum,
-- n has to be short enough to be repeated twice
[n,n..]

이 네 가지 표현 중 하나가 무한한 목록을 생성합니다 n.

매우 구체적인 팁이지만 최대 3 바이트 를 절약 할 수 있습니다 !


4
n전역 인 경우 l=n:l;l길이가 같고 (일부) 더 긴 표현식에서 작동합니다. (하지만 공백이 필요할 수 있습니다.)
Ørjan Johansen

@ ØrjanJohansen 게시물에 통합했습니다. 감사!
완전히 인간적인

10

하나의 결과가 빈 목록 인 경우 더 짧은 조건

일부 condition에 따라 목록 A또는 빈 목록 을 반환하는 조건이 필요한 경우 일반적인 조건부 구문에 대한 짧은 대안이 있습니다.[]C

if(C)then(A)else[] -- the normal conditional
last$[]:[A|C]      -- the golfy all-round-conditional
concat[A|C]        -- shorter and works when surrounded by infix operator
id=<<[A|C]         -- even shorter but might conflict with other infix operators
[x|C,x<-A]         -- same length and no-conflict-guarantee™
[0|C]>>A           -- shortest way, but needs surrounding parenthesis more often than not

예 : 1 , 2


두 번째는 보유 A하고 []스위치.
Ørjan Johansen 님이

@ ØrjanJohansen 고마워요!
Laikoni

1
아하! 그러나 (편안함을 위해 조금 낮음) *>보다 높은 고정력을가집니다 >>.
Ørjan Johansen

9

람다 파싱 규칙

람다 표현에는 실제로 괄호가 필요하지 않습니다. 단지 탐욕스럽게 모든 것을 잡고 있으므로 모든 것이 여전히 구문 분석됩니다.

  • 폐막 장- (foo$ \x -> succ x)
  • 에- let a = \x -> succ x in a 4
  • 줄의 끝- main = getContents>>= \x -> head $ words x
  • 기타..

이 발생하면 1 ~ 2 바이트를 절약 할 수있는 이상한 경우가 있습니다. 나는 \또한 연산자를 정의하는 데 사용될 수 있다고 생각 하므로 이것을 악용 할 때 연산자 바로 뒤에 람다를 작성할 때 공간이 필요합니다 (예 : 세 번째 예).

람다를 사용하는 것이 내가 알아낼 수있는 가장 짧은 곳 의 예 입니다. 코드는 기본적으로 다음과 같습니다.

a%f=...
f t=sortBy(% \c->...)['A'..'Z']

9

let람다로 교체

일반적으로 가드와 바인딩 되거나 어떤 이유로 든 전역 적으로 정의 될 수없는 고독한 보조 정의를 단축 할 수 있습니다 . 예를 들어, 교체

let c=foo a in bar

3 바이트 짧게

(\c->bar)$foo a

여러 보조 정의의 경우 정의 수에 따라 게인이 더 작을 수 있습니다.

let{c=foo a;n=bar a}in baz
(\c n->baz)(foo a)$bar a

let{c=foo a;n=bar a;m=baz a}in qux
(\c n m->qux)(foo a)(bar a)$baz a

let{c=foo a;n=bar a;m=baz a;l=qux a}in quux
(\c n m l->quux)(foo a)(bar a)(baz a)$qux a

일부 정의가 다른 정의를 참조하면 다음과 같이 바이트를 저장하기가 더 어렵습니다.

let{c=foo a;n=bar c}in baz
(\c->(\n->baz)$bar c)$foo a

이것에 대한 주요 경고는 let @ChristianSievers가 지적한 것처럼 다형성 변수를 정의 할 수 있지만 람다는 그렇지 않습니다. 예를 들어

let f=length in(f["True"],f[True])

결과는 (1,1)되지만

(\f->(f["True"],f[True]))length

유형 오류가 발생합니다.


1
그다지 중요하지는 않지만 "의미 적으로 동등한"것은 너무 많은 것을 약속합니다. 우리는 polymorpic을 가지고 let있으므로 할 수 있습니다 let f=id in (f 0,f True). 우리가 이것을 lambda로 다시 쓰려고 시도하면 check를 입력하지 않습니다.
Christian Sievers

@ChristianSievers 사실입니다. 메모 해 주셔서 감사합니다. 내가 편집했습니다.
Zgarb

8

가드를 사용하여 바인딩

명명 된 함수를 정의 할 때 표현식을 가드의 변수에 바인딩 할 수 있습니다. 예를 들어

f s|w<-words s=...

와 동일

f s=let w=words s in ...
f s=(\w->...)$words s

반복 표현식을 저장하려면 이것을 사용하십시오. 식을 두 번 사용하면 간격과 우선 순위 문제로 인해 길이가 6 일 때도 끊어집니다.

(이 예에서 원래 변수 s를 사용하지 않으면 수행하는 것이 더 짧습니다

g w=...
f=g.words

그러나 더 복잡한 표현식을 바인딩하는 것은 사실이 아닙니다.)


이 답변 의 중복 / 특수 사례가 아닙니까?
Lynn

@Lynn 되돌아 보면, 그것은 특별한 경우이지만, 그 대답을 읽을 때 Just예제는 패턴 일치가 표현식에 저장하는 것이 아니라 컨테이너에서 추출하는 것이라고 생각하게했습니다.
xnor

8

비교 (0<$)대신 사용length

목록 a이 목록 보다 긴 경우 테스트 할 때 b일반적으로

length a>length b

그러나 두 목록의 각 요소를 같은 값으로 바꾸고 예를 들어 0두 목록을 비교하는 것이 더 짧을 수 있습니다.

(0<$a)>(0<$b)

온라인으로 사용해보십시오!

괄호 때문에 필요 <$하고, 비교 연산자 ( ==, >, <=다른 경우에 그들이 필요하지 않을 수도 있지만 ...)가 더 바이트를 절약, 같은 우선 순위 레벨 4를 가지고있다.


8

더 짧은 transpose

transpose기능 을 사용하려면 Data.List가져와야합니다. 이것이 가져 오기가 필요한 유일한 함수 인 경우 다음의 foldr정의를 사용하여 바이트를 저장할 수 있습니다 transpose.

import Data.List;transpose
e=[]:e;foldr(zipWith(:))e

길이가 같은 목록 목록에 대해서만 동작이 동일합니다.

나는 이것을 여기에서 성공적으로 사용 했다 .


8

접미사 얻기

scanr(:)[]목록의 접미사를 얻는 데 사용하십시오 .

λ scanr(:)[] "abc"
["abc","bc","c",""]

이것은 tails이후 보다 훨씬 짧습니다 import Data.List. scanr(\_->init)=<<id(Ørjan Johansen이 만든) 접두사를 사용할 수 있습니다 .

λ  scanr(\_->init)=<<id $ "abc"
["","a","ab","abc"]

이것은 바이트를 절약합니다

scanl(\s c->s++[c])[]

아마도 scanl(flip(:))[] "abc"= ["","a","ba","cba"]도 언급 할 가치가 있습니다. 때로는 접두사가 거꾸로되는 것이 중요하지 않습니다.
Lynn

3
Ørjan Johansen은 접두사에 대한 1 바이트 더 짧은 대안을 찾았습니다.scanr(\_->init)=<<id
Laikoni
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.