仕分メソッドaccountとは
各点が含まれる区間を戻す。発端はgroup_by のような処理で、要素数を保ったまま戻り値を得るメソッド(名称募集)
- 各点
distances = [0, 20, 40, 60, 80, 100, 120, 126.153]`
- 区間集合
sections = [
{"距離" => 0, "高さ" => 80.650},
{"距離" => 40, "高さ" => 78.650},
{"距離" => 126.153, "高さ" => 76.065}
]
- 計算結果
=> distances.account(sections.dup, "距離")
{"距離"=>0, :under=>{"距離"=>0, "高さ"=>80.65}, :over=>{"距離"=>40, "高さ"=>78.65}}
{"距離"=>20, :under=>{"距離"=>0, "高さ"=>80.65}, :over=>{"距離"=>40, "高さ"=>78.65}}
{"距離"=>40, :under=>{"距離"=>40, "高さ"=>78.65}, :over=>{"距離"=>126.153, "高さ"=>76.065}}
{"距離"=>60, :under=>{"距離"=>40, "高さ"=>78.65}, :over=>{"距離"=>126.153, "高さ"=>76.065}}
{"距離"=>80, :under=>{"距離"=>40, "高さ"=>78.65}, :over=>{"距離"=>126.153, "高さ"=>76.065}}
{"距離"=>100, :under=>{"距離"=>40, "高さ"=>78.65}, :over=>{"距離"=>126.153, "高さ"=>76.065}}
{"距離"=>120, :under=>{"距離"=>40, "高さ"=>78.65}, :over=>{"距離"=>126.153, "高さ"=>76.065}}
{"距離"=>126.153, :under=>{"距離"=>126.153, "高さ"=>76.065}, :over=>nil}
仕分account処理の考察
-
特徴
- 配列distancesとsectionsで順序(ここでは"距離")が結合基準(距離の小<大)
- 結合基準が一致(==)の場合がmap。そのためmapとは本質的に異なる
-
性能
- 一点に対して区間集合の中のどの区間に属すか、検索すると各点数n × 区間数mの手間
- 一方でaccountは判定毎に比較母数の区間数mが減るので、格段に手間が減る
- 一つ一つ検索するよりも、まとめて与えて判定する方が良い
- ... なのでそれなりに存在価値があるのではなかろうか。
実装はsort_by()を模倣して
判定時の値をブロックの評価結果で指定
class Array
def account(sectionlist, &block)
return self.map{ |distance|
while sectionlist.length > 0 do
under = sectionlist.first()
over = sectionlist.length < 2 ? nil : sectionlist[1];
if (over.nil? || distance < (block_given? ? yield(over): over))
break {:key => distance, :under => under, :over => over}
end
sectionlist.shift();
end
}
end
end
=begin
各点の高さを求めようとすると、accountとmapは本質的に異なるとはいえ、冗長。
=end
puts distances.account(sections.dup){|section| section["距離"]}
puts distances.account(sections.dup){|section| section["距離"]}.map{|distance, under, over|
height = if over.nil?
under["高さ"]
else
grad = (over["高さ"] - under["高さ"])/(over["距離"] - under["距離"])
under["高さ"] + grad * (distance - under["距離"]);
end
{"距離" => distance, "高さ" => height};
}
重み付け関数という位置づけがいいのかもしれない。
def weighted(sectionlist, key, &block)
return self.account(sectionlist){|section| section[key]}.map{|v|
block_given? ? block.call(v[:key], v[:under], v[:over]) : v
}
end
puts
puts distances.weighted(sections.dup, "距離"){|distance, under, over|
height = if over.nil?
... 以下略
{"距離" => distance, "高さ" => height};
}