LoginSignup
19
0

More than 1 year has passed since last update.

明日から(業務で)使えない!ElixirでCode GolfにチャレンジするときのTips

Last updated at Posted at 2022-12-18

この記事は、 Elixir Advent Calendar 2022 その1の19日目の記事です。


Code Golfという遊びをご存知でしょうか?

コードゴルフはコンピュータプログラミング・コンテストの一種。参加者は与えられたアルゴリズムを、可能な限りもっとも短いソースコードで記述することを競う[1]。バイナリサイズではなく、ソースコードの文字数がスコアとなる。

Wikipediaで説明されているように与えられたお題に対してより短い文字数で回答することを競います。
短く記述するためには言語の特性を知り尽くし、動く範囲でできる限りコードを省略することが重要となります。

この記事ではElixirでCode Golfにチャレンジする際のTipsをいくつか紹介したいと思います。
業務では使えないような記述ばっかりですので使わないでくださいね!😂

⛳ どこで遊べるの?

Code Golfにチャレンジできるサイトはいくつかあるみたいですが、僕は以下のサイトで遊んでいます。

問題は84Hole用意されており、対応している言語も45言語あります。
もちろんElixirも対応済みです👍

✨Code Golf Tips in Elixir

🏌️‍♂️関数はモジュールを使わず無名関数で定義する

Elixirでは基本的にはモジュールを定義し、その中に関数を定義していきます。
ただしCode Golfではモジュールは定義しません。
文字数がかなり増えてしまうので、関数を定義する場合は無名関数で実装します

# 🙄long...
defmodule Hoge do
  def fuga(n) do
    IO.puts n
  end
end
Hoge.fuga("hello")

# 🏌️‍♂️short!
f = fn n -> IO.puts(n) end
f.("hello")

また無名関数は以下のように記述することでさらに短くできます

f = & IO.puts&1
f.("hello")

🏌️‍♂️空白や()はなるべく除去する

Elixirは関数に引数を渡すときに()を省略することができます。
() を省略すれば1打スコアを短くできます👍

# 🙄long...
IO.puts("hoge")
Enum.map(1..10, fn i -> i * 2 end)

# 🏌️‍♂️short!
IO.puts "hoge"
Enum.map 1..10,& &1*2

更に文字列やMap、&1などの場合は空白も省略することができます
記号があると空白を省略できる場合が多いです

# 🏌️‍♂️short!
IO.puts"hoge"
&IO.puts&1
f=fn{a,b}->IO.inspect%{"#{a}"=>b}end

🏌️‍インデントはしない

当たり前ですが、インデントを入れるとその分文字数が増えてしまいます。
Code Golfでは読みやすさは考慮する必要がありません。
最終的にはすべて削ってしまいましょう‼️
また文字数的には変わりませんが改行の代わりに;を入れるとワンライナーで書けて短くなる気がするのでおすすめです😂

# 🙄long...
fn
  0 -> 0
  1 -> 1
  n -> div(10,n)
end

# 🏌️‍♂️short!
fn 0->0;1->1;n->div(10,n)end

🏌️‍♂️条件分岐はifを使わずに論理演算を使う

Code Golfのお題を解くために条件分岐が必要になる場面は多いです。
通常ですと、 case cond if などを使うのが普通ですが、Code Golfでは文字数が増えすぎてしまいます。
その場合は論理演算(&& ||)を使うと短くすることができます

# 🙄long...
if rem(n, 2) == 0, do: :even, else: :odd

# 🏌️‍♂️short!
rem(n,2)==0&&:even||:odd

{条件式}&&{真}||{偽} のような形で記述することができます。
(⚠️プロダクトコードで記述するのは絶対にやめましょうw)

ちなみにElixirでFalseになる値は false nil のみで、その他はすべて true になります。
Code Golf観点だと 0 ""false になったほうが嬉しかったですね

🏌️‍♂️変数は文字列になるべく埋め込む

Code Golfでは文字列を出力する問題がたくさんあります。
変数を文字列に埋め込みたい場面も多いですが、その際は文字列に埋め込んだほうが短くなります。

# 🙄long...
"hogehoge"<>n<>"fugafuga"
# => 25 bytes

# 🏌️‍♂️short!
"hogehoge#{n}fugafuga"
# => 22 bytes

ただし変数が接頭、接尾に来る場合は埋め込まないほうが1打短くなります。

# 🙄long...
"#{n}hogehoge"
"hogehoge#{n}"
# => 14 bytes

# 🏌️‍♂️short!
n<>"hogehoge"
"hogehoge"<>n
# => 13 bytes

🏌️‍♂️無名関数で再帰を行う

再帰が必要になる場面もあります。
再帰はモジュール関数にしないと実装できないかと最初は思っていましたが、無名関数でもやる方法があるようでした。

それが再帰関数として呼び出す関数を一旦変数として持っておいて、自分自身に渡すやり方です。

# 🙄long...
defmodule Fibonacci do
  def fib(0), do: 0
  def fib(1), do: 1
  def fib(n), do: fib(n-1) + fib(n-2)
end

# 🏌️‍♂️short!
f=fn 0,_ -> 0; 1,_ -> 1; n,f -> f.(n-1, f) + f.(n-2, f) end
f.(10, f) # => 55

この方法なら無名関数で再帰が記述できるので大幅に打数を短縮することができます。

🏌️‍♂️モジュール関数を複数回使う場合はimportを検討する

EnumモジュールやStringモジュールの関数を複数回使う場合はimportしたほうが短くなります。

# 🙄long...
1..10|>Enum.map(& &1**2)|>Enum.filter(&rem(&1,2)==0)|>Enum.join(",")
# => 68 bytes

# 🏌️‍♂️short!
import Enum
1..10|>map(& &1**2)|>filter(&rem(&1,2)==0)|>join(",")
# => 65 bytes

Enum.が5打、 import Enumが12打(改行含む)なので、 Enum.が3回以上ある場合はimport Enumに切り替えたほうが短くなります 🏌️‍♂️

ただし、EnumStringの両方のモジュールを同時にimportすることはできません。
(一部の関数名がかぶるので)
その場合は使用回数を見てどちらをimportするか決めましょう

🏌️‍♂️パイプ演算子の使い所

Elixirの人気の一つがパイプ演算子(|>)ですが、省略したほうが短くなる場合が多いです。

# 🙄long...
[1,2,3]|> Enum.count|>Integer.to_string

# 🏌️‍♂️short!
Integer.to_string Enum.count [1,2,3]

ただし以下のような引数がある関数の実行結果を引数にとる場合はパイプ演算子を使ったほうが短くなるみたいです

# 🙄long...
f(&1,g(&2,&3))

# 🏌️‍♂️short!
f&1,&2|>g&3

🏌️‍♂️100以上の数字は?dなどで置き換える

Elixirでは文字列のコードポイントを ? で取得することができます。
これを利用することで 100 以上の数字の場合に打数を短くすることができます。

# 🙄long...
1..100

# 🏌️‍♂️short!
1..?d

Elixirで対応しているUnicodeのcodepointは最大で1114111みたいなので 100..1114111の数字は置き換えることが可能です

おわりに

明日から業務で使えないようなTipsを紹介いたしました ✨
Code Golfはチャレンジしてみると結構面白く頭の体操になりますので業務の息抜きなどでチャレンジしてみるといいかもしれません 👍
まわりからも仕事しているように見えるのでおすすめです


参考: https://code.golf/wiki/langs/elixir

19
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
0