ここではPHP7を例にとって書いていきますが、RubyやPythonなどの言語をやっている方でもどう考えるかお聞きしたいです。
数値を任意の情報に変換するための連想配列があるとします。
php
1$kinds = [ 2 1 => "man", 3 2 => "dog", 4 3 => "cat", 5];
下記のように使うことを考えています。
php
1$kind = $kinds[$num];
この連想配列を任意のスクリプトファイルに入れて呼び出せるようにしておきたい場合、どのように置いておくでしょうか。
身近なところで関数に入れているプロジェクトがありました。
php
1function getKinds() { 2 $kinds = [ 3 1 => "man", 4 2 => "dog", 5 3 => "cat", 6 ]; 7 return $kinds; 8} 9// 使用例 10$kinds = getKinds(); 11$kind = $kinds[$num];
使うたびに関数呼び出しをするのが合理的ではないと思います。
下記のように、名前のバッティングを起こさないように、任意のクラスのメンバにするのが妥当だと考えています。
php
1class Util { 2 public $kinds = [ 3 1 => "man", 4 2 => "dog", 5 3 => "cat", 6 ]; 7}
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
ベストアンサー
###まずはRubyで考える
始めにRubyで考えます。
PHPのarrayとは違いRubyでは配列(Array)と連想配列(Hash)は別の物です。そのため、まずはどちらが良いかを考える必要があります。
単純な数値をインデックスとする場合、Arrayは高速であり妥当な選択です。しかし、負の値には対応していませんし、最大値に応じたメモリ容量が必要になってきます。対して、Hashは任意の数値をkeyにでき、必要な分しかメモリは消費されません。しかし、インデックスアクセスという側面ではArrayに劣ります(計算量は同じO(1)ですが)。今回の例では1~3しかないためArrayとした方が良いと考えられますが、負の値や最大値が非常に大きくなる歯抜けのkeyであればHashの方が良いかもしれません。
次に、大事なのは書き換えを不可能にすると言うことです。immutableなデータは予期せぬバグを防止することができます。
以上を踏まえて、考察します。
####定数でArray
Ruby
1# frozen_string_literal: true 2 3module Util 4 KINDS = [ 5 nil, 6 'man', 7 'dog', 8 'cat', 9 ].freeze 10end 11 12num = 1 13kind = Util::KINDS[num] 14p kind
moudle
は名前空間を作成しているだけです。定数にしてかつfreeze
にして配列が変更されないようにします(文字列は最初のマジックコメントでfrozenされています)。配列の0番目は何もないためnil
を入れています。Rubyはインデックスを超えている場合はnil
が返すため、存在確認はnil
で行うべきです。
これは1,2,3と連続した数値であったから問題なかった書き方です。これがもし、1,2,100となっていれば、配列のリテラルがnilで埋まることになり、綺麗なコードとは言えません。
順序の固定された文字列の配列と考えるのであれば、本来インデックスは0から始めるべきでしょう。そうであれば、もっとうまく書くことができます。
Ruby
1# frozen_string_literal: true 2 3module Util 4 KINDS = %w[man dog cat].freeze 5end 6 7num = 0 8kind = Util::KINDS[num] 9p kind
####定数でHash
Ruby
1# frozen_string_literal: true 2 3module Util 4 KINDS = { 5 1 => 'man', 6 2 => 'dog', 7 3 => 'cat', 8 }.freeze 9end 10 11num = 1 12kind = Util::KINDS[num] 13p kind
Arrayとそれほど違いはありません。こちらの利点は、何と言っても数値が負の値でも可能であり、また、0から始まる順番にならんでなくても、一つ一つリテラルで書けるということです。
Ruby
1# frozen_string_literal: true 2 3module Util 4 KINDS = { 5 -1 => 'man', 6 2 => 'dog', 7 10 => 'cat', 8 }.freeze 9end 10 11num = -1 12kind = Util::KINDS[num] 13p kind
####モジュールメソッド
Ruby
1# frozen_string_literal: true 2 3module Util 4 module_function 5 6 def kind_name_by_id(n) 7 case n 8 when 1 9 'man' 10 when 2 11 'dog' 12 when 3 13 'cat' 14 else 15 raise ArgumentError, 'Not found kind' 16 end 17 end 18end 19 20num = 1 21kind = Util.kind_name_by_id(num) 22p kind
完全なメソッドにしてしまいます。この場合の利点は、存在しない場合は例外を投げるなどの動作が可能になると言うことです。
上のコードはどの名前なのかを探すためにcase式を使用しています。if式でも同様のことができるでしょう。柔軟性は高いですが、見た目も良いわけではなく、また、順番に比較するため計算量もO(n)です。3個程度であれば問題ありませんが、数が多い場合は妥当な実装とは言えません。
そこで先ほどのArrayまたはHashと組み合わせます。今回はHashと組み合わせてみます。
Ruby
1# frozen_string_literal: true 2 3module Util 4 KINDS = { 5 1 => 'man', 6 2 => 'dog', 7 3 => 'cat', 8 }.freeze 9 10 module_function 11 12 def kind_name_by_id(n) 13 KINDS.fetch(n) 14 rescue KeyError 15 raise ArgumentError, 'Not found kind' 16 end 17end 18 19num = 1 20kind = Util.kind_name_by_id(num) 21p kind
fetch
は存在しないキーに対してはKeyError例外を発生させます。同じエラーになるようにrescueで救出した後に再度例外を発生させています。
このようにすれば、計算量もO(1)のままですし、対応も別途書いてあるためすっきりした物になります。数が多い場合はこのようにした方が良いでしょう。
KINDS
が定数として定義されており、他からも直接見えてしまうのはよくないと考えるかも知れません。では、次のようにした方が良いのでしょうか?
Ruby
1# frozen_string_literal: true 2 3module Util 4 module_function 5 6 def kind_name_by_id(n) 7 kinds = { 8 1 => 'man', 9 2 => 'dog', 10 3 => 'cat', 11 } 12 kinds.fetch(n) 13 rescue KeyError 14 raise ArgumentError, 'Not found kind' 15 end 16end 17 18num = 1 19kind = Util.kind_name_by_id(num) 20p kind
ローカル変数へアクセスする方法がないため、freezeの必要はありません。これなら、先ほどよりも良いのでは無いかと思うかも知れません。
しかし、これはあまりよくありません。なぜなら、メソッドを呼ぶ出す度にHashが毎回生成されるからです。Rubyのリテラルは定数ではありません。オブジェクトの生成式です。生成コストが小さい数値や再利用されるシンボルやfrozen化された文字列であれば問題ありませんが、[]
や{}
で配列や連想配列を生成することは、Array.new()
やHash.new()
を呼び出しているのと同じです。書き換わることがにimmutableのオブジェクトであれば、公開されても問題ありませんので、定数に入れてしまって何度も利用した方が良いでしょう。
###PHPで考える。
Rubyでの考察が終わりましたので、PHPに戻ります。PHPではどのようにしたら良いのでしょうか?名前空間はmoduleではなくnamespaceを使います。ArrayとHashの区別がないため、arrayを使えば問題なさそうです。では、早速、ベストだと思われる方法を書いてみましょう。
PHP
1<?php 2namespace Util { 3 function kind_name_by_id($n) { 4 $kinds = [ 5 1 => 'man', 6 2 => 'dog', 7 3 => 'cat', 8 ]; 9 return $kinds[$n]; 10 } 11} 12 13namespace { 14 $num = 1; 15 $kind = Util\kind_name_by_id($num); 16 echo $kind; 17}
あれ、先ほどRubyではローカルで連想配列を作ることは駄目出しされたのでは?いいえ、PHPはこれでいいんです。
PHP7.0からは全てがリテラルなarray(immutable array)についてキャッシュを持つようになりました。arrayは毎回作成されず、再利用されます。なので、Rubyのような問題が起こることはありません。
RubyにはないPHPの優れた部分を垣間見えたと思います。PythonとかRubyとか、そんな別言語の事情は参考にせず、PHP特有のPHPにしかない独自の事だけを考えた方が良いかと思います。
投稿2017/06/17 03:26
総合スコア21735
0
それをどれくらいの範囲で参照するか次第かなあ。
このへんはプロジェクトによっても決まりがあるかもですし、
場合によっては(提示された)関数の方がいい場合も?
クラス内でしか参照しないなら、その中で定義してもいいし
全体で参照するなら config 的なものもありでしょう。
さらに、複数プロジェクト間で使うならデータベースもあり。
ちなみに、関数にするなら全体を返すんじゃなくて
id を指定して text を返すのが好みかなあ。
エラー処理も含めてね。
投稿2017/06/16 09:44
総合スコア7458
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
具体案でなく、検討項目についてだけ述べます。
以下の様な事を考える必要があります。
- 連想配列の内容をどこで管理するか?
例:
- プログラムコード中に定数宣言する
- 外部ファイル (ini ファイル、csv/json/yaml ファイル、 Database のテーブル...,)
- 環境変数で与える
- 変換を利用する側のコードが、連想配列そのものを利用するようにするのか、
(int -> 値) のメソッドとして利用するようにするのか?
連想配列のサイズが膨大でメモリー使用量が問題になりそうなら、Database の利用を検討するとか、
その(int -> 値) の変換機能そのものを別サーバーでサービスするとかを検討することが必要になります。
例: 郵便番号 -> 住所 の変換
連想配列の値の保守のことも重要です。
変更される可能性があるなら、書き換えの管理、書き換え後のテストがし易いようしておくことが必要です。
実行環境毎に変化するようなら、コードに固定値を記載することはできません。
外部ファイルにするか、環境変数で管理ようなことが必要になります。
連想配列そのものを利用するようにした場合は、その配列内容を外部側から書き換えできないようにガードすることが必要です。
(int -> 値) のメソッドを用意するようにした場合は、その呼出コストがを検討する必要があります。
キャッシュ化するなどの工夫が必要なる場合もあるかもしれません。
ともかく、データの特性、利用する側の特性 を分析し、メモリー量、計算量、処理時間といったことのトレードオフを踏まえることが必要です。
さらに、利用するプログラム言語環境内での実装方法も加味して決定していくことになるとおもいます。
投稿2017/06/18 10:25
総合スコア22324
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
普通にこうすればよいのでは?
ちょっと雑なので必要な仕様は追加して下さい
PHP
1class Util 2{ 3 public $kinds = []; 4 function __construct() 5 { 6 $this->initKinds(); 7 } 8 function initKinds() 9 { 10 $this->kinds=[ 11 1 => "man", 12 2 => "dog", 13 3 => "cat", 14 ]; 15 } 16 function setKind($a,$b) 17 { 18 $this->kinds[$a]=$b; 19 } 20 function getKind($a) 21 { 22 return $this->kinds[$a]; 23 } 24 25} 26$u=new Util; 27print $u->getKind(1)."<br>"; 28$u->setKind(1,'xxx'); 29print $u->getKind(1)."<br>"; 30print $u->getKind(2)."<br>"; 31
投稿2017/06/16 13:27
総合スコア114827
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/06/17 14:55