Haskellでのゴルフのヒント


73

Haskellでゴルフをするための一般的なヒントは何ですか?私は、少なくともHaskellに特有のゴルフ問題全般のコーディングに適用できるアイデアを探しています。回答ごとに1つのヒントのみを投稿してください。


Haskellでゴルフをするのが初めての場合は、Haskellのゴルフ規則ガイドをご覧ください。また、専用のHaskellチャットルーム:Of Monads and Menもあります。


1
今までの回答の数を見て、Haskellがコードゴルフに適した言語であるかどうか疑問に思っていますか?
Animesh 'the CODER' 14年

10
回答ごとに1つのヒントしかありません。また、すべての言語はゴルフに適した言語です。常に勝つことを期待しないでください。
unclemeat 14

6
@unclemeatこの方法では、同じものが同じ答えで書かれているため、悪いものを支持せずに、良いものをトップに支持することができます。
MasterMastic 14年

3
特別なリクエスト、文字列圧縮。
Jアトキン

これはおそらく、anwerとしては適していませんが、私はまだそれをここに追加したいんだ:wiki.haskell.org/Prime_numbers_miscellaneous#One-liners
flawr

回答:


45

二項関数の代わりに中置演算子を定義する

これにより、通常、定義または呼び出しごとに1つまたは2つのスペースが節約されます。

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

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

1バイトの事業者のための利用可能なシンボルであり!#%&、と?。他のすべてのASCII句読点は、Preludeによって演算子として既に定義されているか(など$)、Haskellの構文で特別な意味を持っています(など@)。

5つを超える演算子が必要な場合、などの上記の組み合わせ!#、または次のような特定のUnicode句読文字を使用できます(UTF-8ではすべて2バイト)。

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

11
注:これは、3つ以上の引数を持つ関数にも使用できます。(x!y)z=x+y*zそして(x#y)z u=x*z+y*u予想通りの両方が動作します。
ズガーブ

3
これは、たとえば次の\f g(!)x y->f g!x y代わりに、関数の引数にも使用できます。\f g j x y->j(f g)(x y)
Esolanging Fruit

2
括弧を使用する必要がある場合は、単項関数を二項演算子に変更すると有益な場合があります- g x=…;g(f x)より長い_?x=…;0!f x
-Angs

29

必要に応じて無意味(または-free)表記を使用する

多くの場合、1つまたは2つのパラメーターを持つ関数は、ポイントフリーで記述できます。

そのため、要素が交換されているタプルのリストの検索は、単純に次のように記述されます。

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

例:

  • リストを2回繰り返す

    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
リン

を使用=<<すると、インポートが強制されますControl.Monad。他の理由でそれが必要ない場合は、引数を交換して使用する>>=方がより簡潔に思えます。
dfeuer

OTOH、Data.Traversableとにかく必要な場合、デカルト積の例はに短縮できますfor["Hh","io",".!"]id
dfeuer

2
(=<<)実際はPreludeにあります!私はそれをたくさん使いました。
リン

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
また、&&リスト内包の内部ではなく、複数のガードを使用します。
ジョンドヴォルザーク

良い1月-あなたは、私はそれに投票よ、答えにそれをしなければならない
bazzargh

5
最初の例は、さらに短くすることができますTrue=>1>0
ジョンドヴォルザーク

1
最初の例では、私はあなたが意味すると仮定しますf a=if a>0 then 3 else 7
チョイス

Guardには、引数がない場合でも機能します。
アカンカ

24

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

人々はしばしばこの関数が存在することを忘れます-それはすべての標準入力を取得し、それを(純粋な)関数に適用します。私はよくmain-codeの行に沿ってコードを見る

main=getContents>>=print.foo

しながら

main=interact$show.foo

かなり短いです。プレリュードにあるので、インポートする必要はありません!


24

GHC 7.10を使用

このようなものを含むGHCの最初のバージョンは、2015年3月27日にリリースされました。

これは最新バージョンです。Preludeには、ゴルフに役立ついくつかの新しい追加機能が追加されました。

演算子(<$>)(<*>)

これらの便利な演算子がData.Applicative作成されました!<$>はだけなfmapので、どこにでも、どこでも置き換えてmap f x、バイトを取り戻すことができます。fmap f xf<$>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ます。

これは、多くの場合にすてきな代替手段ですreplicate4<$[1..n]よりも短くなっていますreplicate n 4

Foldable / Traversableの提案

以下の機能は、リストの作業[a]から一般的なFoldableタイプへの作業から解除されましたt a

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

つまり、Maybe a「最大1つの要素を持つリスト」のように動作するようになりました。たとえばnull Nothing == True、またはsum (Just 3) == 3。同様に、length値に0を返しNothingJust値に1を返します。書く代わりにを書くx==Just yことができますelem y x

タプルにそれらを適用することもできます\(a, b) -> [b]。これは、最初に呼び出した場合と同じように機能します。それはほとんど完全に役に立たないが、or :: (a, Bool) -> Boolより短い1文字でありsndelem bより短い(==b).snd

モノイド関数memptymappend

命の恩人になることはあまりありませんが、型を推測できる場合memptyNothing、よりも1バイト短くなります。


5
+1 '<$> `について聞い<*>て、プレリュードに参加できてうれしいです!それは、ゴルフをコード化していないときでも有用であるべきです(適用者はそのような長い言葉です)。
アンク-morpork

フラット交換に関する警告:言語バージョンがチャレンジよりも新しい場合、ソリューションは勝ちません。既存の回答または回答を更新する場合は、既存のソリューションを上書きしないでください。付録を書きます。
ジョンドヴォルザーク

4
そこに面白いが[1..2]あります。それはただ[1,2]
誇りに思ってhaskeller

2
同じバージョンで、リスト<*からも取得しました。これは異なっているかです。これもこの時点で短くなりました。Applicativexs <* ys == concatMap (replicate (length ys)) xsxs >> ysxs *> ysconcat (replicate (length ys)) xspurereturn
アンズ

2
<>代わりに使用できるようにmappendなりました(GHC 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 14年

2
これは良い例ではありませんf=max 10
誇りに思ってhaskeller 14

@MasterMasticこれはif(true)他の言語で書いているだけです。プレリュードでは、そうでなければ実際にはブール値Trueです。
誇りに思ってhaskeller 14

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"]

しかし、時々便利になる奇妙な使い方がいくつかあります。1つは、リストの理解に<-矢印を含める必要がないことです。

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が抽出さllます。つまり、と同じ数の0のリストが作成されます。


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

これにより、空のリスト(infixとして使用)1に含まれる要素の数lだけのfリストが作成さ<$>mapます。sumこれらの要素を数えるために適用します。

比較:

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

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

定数は、パターンマッチの一部として使用できます。これにより、最初のエントリがであるすべてのタプルの2番目のエントリが抽出されます0


これらはすべて、変数の値ではなく、実際の定数リテラルを必要とすることに注意してください。たとえば、外部バインディングが上書きされるため、ではなく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 xssequence単独で使用する場合でも、それより長くなりmapM idます。

2)(または)
を使用して機能を組み合わせる(>>=)(=<<)

関数のモナドバージョン(>>=)は次のように定義されます。

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

パイプラインとして表現できない関数を作成するのに役立ちます。例えば、\x->x==nub xより長いnub>>=(==)、そして\t->zip(tail t)tより長いですtail>>=zip


+1-関数のモナドインスタンスが存在することすら知りませんでした。それは非常に便利かもしれません:)
ジュール

2
補足:実装の一部でApplicativeありMonad、実装もありませんが、pureこれはconst以前よりも短く、実際に私を助けてくれました。
1

14

引数は定義よりも短くすることができます

私はヘンクマによって非常に奇妙な方法でアウトゴルフされました

f回答内の補助関数が、回答内の他の場所では使用されず、f一度呼び出される演算子を使用している場合、その演算子をの引数にしfます。

この:

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

これより2バイト長い:

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

12

cons演算子(:)を使用します

リストを連結するときに、最初の長さが1の場合は:代わりに使用します。

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

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

4
それは右結合ですので、あなたは、例えば、リストの先頭に単一の項目の任意の数のためにそれを使用し続けることができることは注目に値する1:2:3:xのではなく[1,2,3]++x
ジュール

11

バックティックをあまり頻繁に使用しないでください。バックティックは、接頭辞関数のセクションを作成するための便利なツールですが、誤用されることもあります。

誰かがこの部分式を書いているのを見たとき:

(x`v`)

ただと同じv xですが。

もう1つの例は(x+1)`div`ydiv(x+1)y

私はそれが周りに起こる見るdivelem、これらの機能は通常、定期的なコードで中置として使用されているので、より頻繁に。


プレフィックス関数のセクションを作成するつもりはありませんか?
チョイス

@Cyoceはい、もちろん
誇り高いhaskeller

11

パターンガードを使用する

これらは、let定義している関数の引数を分解するa またはlambda よりも短いです。これはfromJustfromのようなものが必要なときに役立ちます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のtipを参照してください。


1ドル記号を必要としない、そしてこの変更は、この長と次のスニペットが同じになるようだまあ、ラムダ
誇りhaskeller

1
私はe実際には1つのトークンではなく、その$前に必要な長い式であると仮定していますが、これは通常そうです。
リン

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

@ Potato44これはData.Boolにあり、インポートにバイトがかかります。
xnor

11

マイナス記号の使用

マイナス記号-は、多くの構文規則にとって厄介な例外です。このヒントでは、Haskellで否定と減算を表すいくつかの簡単な方法をリストします。何か見落とした場合はお知らせください。

否定

  • 式を否定するにはe、ただやる-e。たとえば、-length[1,2]を与え-2ます。
  • 場合eでも適度に複雑で、あなたの周りに括弧が必要になりますeが、あなたは通常約それらを移動することでバイトを保存することができます-length(take 3 x)よりも短くなっています-(length$take 3 x)
  • e前に、=または固定度が6未満の中置演算子がある場合、スペースが必要です:がより小さいかどうかをf= -2定義fおよびk< -2テストします。固定度が6以上の場合、括弧が必要です:gives 。通常は、これらを取り除くためにものを並べ替えることができます。たとえば、代わりにdoを実行します。k-22^^(-2)0.25-k>2k< -2
  • 同様に、!オペレータ、次いで-a!bとして解析される(-a)!bの定着性があれば!最大で6である(そう-1<1与えるTrue)、及び-(a!b)それ以外の場合は(そう-[1,2]!!0与えます-1)。ユーザー定義の演算子とバックティック関数のデフォルトの固定度は9であるため、2番目の規則に従います。
  • 否定を関数に変換する(mapetcで使用する)には、セクションを使用します(0-)

減算

  • 減算する関数を取得kするには(-k+)、を追加するセクションを使用します-kkかなり複雑な表現でさえありえます:(-2*length x+)期待通りに動作します。
  • 1を引くpredには、両側にスペースが必要な場合を除き、代わりに使用します。これは稀であり、通常で発生untilするので、またはユーザ定義関数map pred xに置き換えることができるpred<$>xiterate pred xによって[x,x-1..]。また、f pred xどこかにある場合は、fとにかく挿入関数として定義する必要があります。このヒントを参照してください。

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)

最初のブランチhとの間には迷惑な空白がありpます。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

ガードは必須であり、それ以外の場合は浪費されるため、これを価値あるものにするためにはほとんど必要ありません。ペアのペアを保存するか、2回使用される長さ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]
ライコニ

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..]

これらの4つの式のいずれかは、の無限リストを生成しnます。

これは非常に具体的なヒントですが、最大3バイト節約できます!


4
nがグローバルの場合、l=n:l;l長さは同じで、(一部の)より長い式で機能します。(ただし、空白が必要な場合があります。)
ØrjanJohansen

@ØrjanJohansen私はそれを投稿に組み込みました。ありがとう!
完全に人間

10

1つの結果が空のリストである場合のより短い条件

条件に応じてリスト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

例:12


もう一つは持ちA[]切り替えました。
Ørjanヨハンセン

@ØrjanJohansen修正、ありがとう!
ライコニ

1
あぁ!しかし、*>より高い固定性がある>>(まだ少し低い快適さのために。)
Ørjanヨハンセン

9

ラムダ解析ルール

ラムダ式は実際に括弧を必要としません-それはむしろ貪欲にすべてをつかむので、全体がまだ解析されます、例えばまで

  • 閉じ括弧- (foo$ \x -> succ x)
  • で- let a = \x -> succ x in a 4
  • 行末- main = getContents>>= \x -> head $ words x
  • 等..

が発生し、これにより1〜2バイト節約できる奇妙なエッジケースがいくつかあります。\また、演算子の定義にも使用できると考えているため、これを利用する場合、演算子の直後にラムダを記述するときにスペースが必要になります(3番目の例のように)。

ラムダを使用することが私が理解できる最短のものであった場所の例を次に示します。コードは基本的に次のようになります。

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
それはめったに重要ではありませんが、「意味的に同等」は少し多すぎると約束します。多形性letがあるので、できますlet f=id in (f 0,f True)。これをラムダで書き直そうとすると、型チェックは行われません。
クリスチャンシーバーズ

@ChristianSieversそれは本当です、メモをありがとう。私はそれを編集しました。
Zgarb17年

8

ガードを使用してバインド

名前付き関数を定義する場合、ガード内の変数に式をバインドできます。例えば、

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

と同じ

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

これを使用して、繰り返し式を保存します。式が2回使用されると、長さ6でさえ壊れますが、間隔と優先順位の問題がそれを変更する可能性があります。

(例では、元の変数sが使用されていない場合、実行する方が短くなります

g w=...
f=g.words

ただし、より複雑な式をバインドする場合はそうではありません。)


これは、この答えの重複/特別なケースではありませんか?
リン

@Lynn振り返ってみると、それは特別なケースですが、その答えを読んだとき、このJust例は、パターンマッチングが式に格納するのではなく、コンテナから抽出するためだと思いました。
xnor

8

比較の(0<$)代わりに使用length

リストaがリストよりも長いかどうかをテストする場合、b通常は次のように書きます。

length a>length b

ただし、両方のリストの各要素を同じ値に置き換えて(例:)0、次にこれらの2つのリストを比較する方が短い場合があります。

(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付けることができます(ØrjanJohansenにより発見)。

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

これはバイトを節約します

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

おそらくscanl(flip(:))[] "abc"= ["","a","ba","cba"]も言及する価値があります。場合によっては、プレフィックスが後方にあることは重要ではありません。
リン

3
Ørjanヨハンセンは、接頭辞のために1バイトの短い方の代替が見つかりました:scanr(\_->init)=<<id
Laikoni
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.