読者です 読者をやめる 読者になる 読者になる

せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【プログラミング】良い命名規則とは。問題指向の命名規則。

 
だんだんコードがよくなってきたけど、
さらに学ぶことはあるはずだ、、と思ってCODE COMPLETEを読んでみる。

Code Complete第2版〈上〉―完全なプログラミングを目指して

Code Complete第2版〈上〉―完全なプログラミングを目指して


 
10章11章を読んだ。

発見したことをいくつか。
自分の解いている問題「宣教師と人食い問題」を例にとってメモ。
 
現在のコードでは、「移動した人数の合計」をmoverとしている。
 

数を表す変数はnumberOfXXXX

ふんふん。確かにそのほうが分かりやすい。

number_of_movers

かな。
 

適切な長さにする

最長でも10~16文字になるよう変数名を工夫する。
「number_of_movers」は問題ないけれど、
ほかにも「~の数」が出てきた場合に長くなりすぎる懸念がある。
こういうときはnumをつかう

num_movers

か。

 

numは混乱のもと

総数を表すのはcountまたはtotalを使う。
特定の対象をあらわすときはindexを使う(customerIndexなど)

total_mover

か。

なるほど。

 

問題指向の名前

社員データのレコードにはinputRecまたはemployeeDataという名前をつけることが出来る・・・ employeeDataの方は計算ではなく問題に言及しいているので良い

なるほど。
自分のコードも改善点があるかもしれない。

 

mover -> total_mover
results -> answers
x -> state

 
よし!これでどうだ!
total_moverという変数名から移動の合計を表しているのが明らかになったので、
コメントも省略できた。

def cases
  answers = ["000000"] #人人人食食食が左岸
  6.times do |i|
    copy = answers.dup
    copy.each do |state|
      new_state = state.dup
      new_state[i] = "1"
      answers << new_state
    end
  end
  answers
end

#食べられないケースだけ抽出
def sieve(cases)
  answers = []
  cases.each do |caze|
    caze_to_int = caze.split("").map{|a|a.to_i}
    a = caze_to_int[0,3].inject(:+)
    b = caze_to_int[3,3].inject(:+)
    #全人間がどちらかの端に存在 もしくは 左岸&&右岸がOK
    answers << caze if (a==3 || a==0) || ((3-a)>=(3-b) && a>=b)
  end
  answers
end

def possible?(state, next_state)
  return false if state == next_state
  total_mover = 0
  if @history.length%2 == 0 #右から左へ
    state.each_with_index do |n, i|
      if n == 0
        return false if next_state[i] != 0 #0->1(右岸に移動)は出来ない
      else
        total_mover += 1 if next_state[i] != 1
      end
    end
    return false if total_mover > 2 #ボートの定員は2名
  else #左から右へ
    state.each_with_index do |n, i|
      if n == 1
        return false if next_state[i] != 1 #1->0(左岸に移動)は出来ない
      else
        total_mover += 1 if next_state[i] != 0
      end
    end
    return false if total_mover > 2
  end
  true
end
def dejavu?(state)
  @history.each_with_index do |previous_string, i|
    previous = previous_string.split("").map{|a|a.to_i}
    p1 = previous[0,3].inject(:+)
    p2 = previous[3,3].inject(:+)
    s1 = state[0,3].inject(:+)
    s2 = state[3,3].inject(:+)
    return true if (p1 == s1) && (p2 == s2) && (i%2 == (@history.length)%2)
  end
  false
end

@history = []
@patterns = sieve(cases)
def _solve(state)
  state_to_string = state.join("")
  @history << state_to_string
  return true if state_to_string == "111111"
    @patterns.each do |pattern|
      next_state = pattern.split("").map{|a|a.to_i}
      if possible?(state, next_state) && !dejavu?(next_state)
        return true if _solve(next_state) == true
      end
    end
  @history.pop
end

def solve_missionaries_cannibals(init)
  init_to_int = init.split("").map{|a|a.to_i}
  _solve(init_to_int)
  return @history
end

p solve_missionaries_cannibals("000000")