孤独プログラマー譚

孤独死が近い。

Ruby クロージャを使ってインスタンス変数を減らす

インスタンス変数は悪である。
だって、クラス内のどこからでも、値を改変できるから。


そこで、インスタンス変数を減らす手立を考えてみた。


以下、ゲッターもどき。
構造体を返す。構造体はラムダをgetメンバに持っている。
ラムダはクロージャになる。変数immutable_varは保持される。
変数の中身(yield)は、後ほどブロックで渡す。

def make_getter
  immutable_var = yield
  struct = Struct.new(:get)
  struct.new(
    -> { immutable_var }
  )
end


以下、ゲッター兼セッターもどき。
getメンバ、setメンバを持つ。もちろんクロージャ

def make_accessor
  immutable_var = yield
  struct = Struct.new(:get, :set)
  struct.new(
    -> { immutable_var },
    -> new_var { immutable_var = new_var }
  )
end


以下で変数の中身を定義する。
'白石麻衣'、'平手友梨奈'となっているブロックの中身は、がっつり処理を書いてよい。
それら変数をまとめて、こちらも構造体で返す。

def make_immutables
  struct = Struct.new(:nogi, :keyaki)
  struct.new(
    make_getter { '白石麻衣' },
    make_accessor { '平手友梨奈' },
  )
end


コンストラクタで、上記構造体をインスタンス変数に入れる。インスタンス変数を使ってしまうが、これはしょうがないと思う。

def initialize
  @immutables = make_immutables
end


変数を呼び出す。
呼び出し方が冗長な気もするが、憎きインスタンス変数を減らせたので、良しとする。

def execute
  p @immutables.nogi.get.call # 白石麻衣
  p @immutables.keyaki.get.call # 平手友梨奈
  @immutables.keyaki.set.call('長濱ねる') # 長濱ねるをセット
  p @immutables.keyaki.get.call #長濱ねる
end


うーん、他に良い方法あるのかなぁ…。

Ruby ブロック付きメソッドの活用

普通は、スクリプトを読みながら、どういう順序・条件で処理が進むのかを調べる。

それがつらい。処理の詳細を見ないと、実行順や分岐の条件が分からないのがつらい。

だから、実行順や分岐の条件を、処理の詳細から分離する。

OOPのTemplateパターンやStateパターンを使えば、実現できる。だが、他に良い方法は無いだろうか。

if group == '乃木坂46'
  func1
  func2
  func3
elsif group == '欅坂46'
  func1
  func3
  func4
end

上記のように書けば、実行順と分岐が分離できたことになる。

うーん、でも何と言うか…。メソッド間の結合が弱いというか。

func1 と func2 の順番を逆にすることもできるし、間に処理をスッと挟むことも出来る。簡単に改変できるのが気持ちが悪い。

そこで、以下のような形を考えた。

def f1
  p 1
  yield
end

def f2
  p 2
  yield
end

def f3
  p 3
end

f1 do
  f2 do
    f3
  end
end

# 1
# 2
# 3

ブロック渡しをする。PHPJavaScriptだと、無名関数でネストさせることになる。

副次的なメリットとして、次のメソッドに渡したい引数を、返り値に含める必要がない。

# 返り値経由で渡す
result = func1
func2 result[:arg1]

# 返り値に含める必要がない
def func1
    arg1 = 'foo'
    yield arg1
end

func1 do |arg1|
    func2 arg1
end


うーん、イマイチかなぁ…。

CoffeeScript Privateメンバ

= で定義すれば、private
: で定義すれば、public
となる。

Privateメンバは、コンストラクタ関数内のローカル変数として、
クロージャによって保持されている…と考えることが出来る。

class Sony
  # public
  pubVar: 'nogi46'
  pubFunc: ->
    console.log '乃木坂46'

  # private
  priVar = 'keyaki46'
  priFunc = ->
    console.log '欅坂46'
sony = new Sony()

console.log sony.pubVar # nogi46
sony.pubFunc() # 乃木坂46

console.log sony.priVar # undefined
sony.priFunc() # エラー

AltJS、ECMA6といっても、JavaScriptの根本的な概念は変わっていないので…。
ECMA5、勉強しといて良かったよ!

今さらCoffeeScriptでクロージャ

仕事でCoffeeScriptを使っている。

練習も兼ねて、クロージャを書く。

CoffeeScriptRubyと似ている…。
素のJavaScriptと比べて、色々と省略できすぎるため、初見では何を書いているかさっぱり分からない。


引数を2倍にする関数、3倍にする関数を返す。

factory = (type, num) ->
  {
    double: ->
      num = num * 2
      console.log num
    triple: ->
      num = num * 3
      console.log num
  }[type]

numは、関数スコープの外側に保持される。
double関数, triple関数はそれぞれクロージャとなる。


素のJavaScriptに書き換えると、以下になる。

var factory = function(type, num) {
  return {
    double: function() {
      num = num * 2
      console.log(num)
    },
    triple: function() {
      num = num * 3
      console.log(num)
    }
  }[type]
}

 

クロージャをそれぞれ実行。
変数numが保持されていることが分かる。

double = factory('double', 1)
double() # 2
double() # 4
double() # 8

triple = factory('triple', 3)
triple() # 9
triple() # 27
triple() # 81

elseif・switchは撲滅して連想配列

前回に引き続き、今回も同じテーマで。

以下、憎くて仕方ないelseif文。

id = 'nogi'

if id == 'nogi' then
  name = '乃木坂46'
elsif id == 'keyaki' then
  name = '欅坂46'
else
  name = 'ソニーミュージック'
end

p name # 乃木坂46

本当にキモい。
たいした処理でもないのに、なぜ、こんなに行数を使うのか理解できない。

そこで、今日、良い記法を思いついた。

[ Ruby ]

id = :keyaki

name = {
  nogi: '乃木坂46',
  keyaki: '欅坂46',
}[id] || 'ソニーミュージック'

p name # 欅坂46

存在しない id を指定すると、左辺が nil になる。
結果、右辺が実行される。短絡評価。

PHPで書くと、

<?php
$id = 'keyaki';

$name = [
    'nogi' => '乃木坂46',
    'keyaki' => '欅坂46',
][$id] ?? 'ソニーミュージック';

echo $name; // 欅坂46

PHP7の ??(Null合体演算子)を使うとスマート。


うーん、かなりイケてると思うんだが…。

elseifやswitch文は連想配列に置き換えてポリモーフィズム

elseifやswtich文は出来るだけ避けたい。
一連のスクリプトから状態を排除したいし、後々の改修で誰かがネストしまくるのが想像できる。

置き換えるには、Stateパターン等を使う必要があると思っていたが、実は連想配列で解決できることに気付いた。

以下、よく見るコード。Rubyで書く。

who = 'shizuka'
if who == 'dora' then
  p '僕ドラえもん'
elsif who == 'nobi' then
  p '僕のび太'
elsif who == 'shizuka' then
  p '私しずか'
end

とにかくキモい。このメソッド内で、dora / nobita / shizuka の分岐が、他に何度も繰り返されるのが目に浮かぶ。


これを連想配列にする。

hash = {
  dora: ->{ p '僕ドラえもん' },
  nobi: ->{ p '僕のび太' },
  shizuka: ->{ p '私しずか' },
}
who = :nobi
hash[who].call

ポリモーフィズムにもなっていて、結構イケてると思うんだけど…。

この、簡単に手を入れられない(深いネストを書けない)ところが良い。


実はelseif文は、連想配列に書き換えられる、という気付きでした。ちゃんちゃん。

Ruby 委譲

Rubyの便利な委譲な仕組み。

一つ目。Forwardableモジュール。

class Sony
  extend Forwardable

  def_delegators :@keyaki, :discord

  def initialize(keyaki)
    @keyaki = keyaki
  end
end

class Keyaki
  def discord
    print '僕は嫌だ!'
  end
end

sony = Sony.new(Keyaki.new)
sony.discord # 僕は嫌だ!

def_delegatorsメソッドの引数に、委譲先のオブジェクト、委譲するメソッドを指定。

Sonyはdiscordメソッドを持っていないにもかかわらず、実行できている。
内部では、Keyakiオブジェクトが、discordを実行している。


二つ目。method_missing。

これは、PHPのマジックメソッド__call()と似たようなもの。

class Sony
  def initialize(keyaki)
    @keyaki = keyaki
  end

  def method_missing(name, *args)
    @keyaki.send name, *args
  end
end

class Keyaki
  def discord
    print '僕は嫌だ!'
  end
end

sony = Sony.new(Keyaki.new)
sony.discord # 僕は嫌だ!

メソッド名を指定しなくていい分、こっちの方が楽かも。


何とかここらへんをうまく使って、Proxyパターンを楽に実装していきたい。