array_mapの再帰処理がうまく行かない

このQ&Aのポイント
  • array_mapを使用して再帰的な処理を試みていますがうまくいきません。
  • nullを指定文字列に置換する関数を作成しましたが、array_mapが正しく動作していないようです。
  • 対処方法を教えていただけませんか?
回答を見る
  • ベストアンサー

array_mapの再帰処理がうまく行かない

長文で失礼します。 array_mapでの再帰処理がうまく行かないのでどこが間違っているか教えてください。 まず、このような配列があります。配列の中に配列があります。 $ary = array(1, 2, null, array("a", null, "c")); この配列の中のnullを"なし"という文字列に変換したいです。 array_mapを使って再帰的にやってみました。 まずはうまく行ったコードから。 ------------------------------------------------------ $ary = array(1, 2, null, array("a", null, "c")); var_dump(null2Nashi($ary)); // nullを"なし"に置換する関数 function null2Nashi(  $in_array ){  if(is_array($in_array)){   return array_map("null2Nashi", $in_array);  } else {   if ($in_array === null){    $in_array = "なし";   }   return $in_array;  } } ------------------------------------------------------ 結果はnullが"なし"に変換されました array (size=4)  0 => int 1  1 => int 2  2 => string 'なし' (length=6)  3 =>   array (size=3)    0 => string 'a' (length=1)    1 => string 'なし' (length=6)    2 => string 'c' (length=1) そしてこの"なし"をコード内で指定するのではなく引数で指定したいと思って無名関数を使って以下のコードにしました。 ------------------------------------------------------ $ary = array(1, 2, null, array("a", null, "c")); var_dump(null2Str($ary, "なし")); // nullを指定文字列に置換する関数 function null2Str(  $in_array, // null値を含む配列  $in_str // null値を変換したい文字列 ){  $n = function($n_array) use($in_str){   if(is_array($n_array)){    return array_map($n, $n_array); //…(1)   } else {    if ($n_array === null){     $n_array = $in_str;    }    return $n_array;   }  };  return $n($in_array); } ------------------------------------------------------ 結果はnullは何も変換されませんでした。 array (size=4)  0 => int 1  1 => int 2  2 => null  3 =>   array (size=3)    0 => string 'a' (length=1)    1 => null    2 => string 'c' (length=1) どうやら(1)のarray_mapが動作していないようです。要素を分解せずに$nの無名関数に渡さずにそのまま第2引数を返しているだけみたいです。 何か対応方法があるでしょうか? どうぞよろしくお願い致します。

  • suffre
  • お礼率76% (2013/2633)
  • PHP
  • 回答数3
  • ありがとう数3

質問者が選んだベストアンサー

  • ベストアンサー
noname#244856
noname#244856
回答No.3

あー、なんで誤りに気づかなかったか分かりました。php.iniの設定で一部のエラーが隠蔽されているからです。おそらく error_reporting = E_ALL & ~E_NOTICE になっているんじゃないですかね。ところがE_NOTICEはデバッグ上非常に重要なエラーで、これを隠蔽してしまうことはデバッグ作業を困難にします。よって error_reporting = E_ALL | E_STRICT にしてすべてのエラーを表示させてください。恒久的ではなく、PHPスクリプト実行中に一時的に変更したい場合はerror_reporting関数を使って error_reporting(E_ALL | E_STRICT); としてください。また、下記リンクで未定義とNULLの違いの再確認をお願いします。 Qiita - 【PHP入門講座】 NULLと未定義の違い http://qiita.com/mpyw/items/0a4ea0bc9a695da33f0c さて、先ほどのコードを実行してみると分かるのですが http://ideone.com/ZrWoN2 このように PHP Notice: Undefined variable: n が発生してしまっていますね。そしてその部分はNULLと見なされ、一見array_mapのコールが失敗するように思われるのですが…実は、array_mapはNULLも第1引数に取ることが出来るのです。 PHP Manual - array_map http://php.net/manual/ja/function.array-map.php 以上の例4が該当しますね。

suffre
質問者

お礼

ありがとうございます! たしかにerror_reporting = E_ALL & ~E_NOTICEとしていました…。 NULLと未定義の件も了解しました。 今回の件でいろいろ見直したほうが良さそうですね。 重ね重ねお礼を申し上げます。

その他の回答 (2)

noname#244856
noname#244856
回答No.2

既存のコードのuse句での受け渡しについてですが、これだと無名関数スコープ内から無名関数自身を参照することが出来ないので、 $n = function($n_array) use($in_str) { ... } としている部分を $n = function($n_array) use($in_str, &$n) { ... } にする必要があります。

suffre
質問者

お礼

ありがとうございます! 無名関数の部分、そうだったのですね・・・。 たしかにuse($in_str, &$n)としましたらうまく動作しました。 これはちょっと気づかなかったです。

noname#244856
noname#244856
回答No.1

反則技かもしれませんが、array_walk_recursiveではダメですか?array_mapは値を返す関数ですが、array_walk・array_walk_recursiveは参照渡しで値を編集する関数ですね。array_walkはforeachとほとんど出来ることに差はないですが、array_walk_recursiveは単純なforeachには出来ない強力な特長を持っています。 $array = array(1, 2, null, array('a', null, 'c')); array_walk_recursive($array, function (&$v) {  if ($v === null) {   $v = 'なし';  } });

suffre
質問者

お礼

ありがとうございます! array_walk_recursive、知らなかったです。 PHPは本当に便利な関数が多いですね。 これを機にもっと勉強しようと思います^^

関連するQ&A

  • 再帰的に処理をする方法

    まとめてサニタイズする方法として下記のように書いてみました。 引数が配列でなければ問題ないのですが、配列だとこんなエラーが出ます。 Warning: array_map() expects parameter 1 to be a valid callback, function 'sanitize' not found or invalid function name in ファイル名 foreachでまわすのも考えましたが、できるならスマートにと思っているのですが どうすれば再帰的な処理ができるでしょうか? $valid = new Valid(); $array = array( 'あああ','いいい','ううう', ); $valid->sanitize( $array ); class Valid{ public function sanitize( $str ){ if ( is_array( $str ) ) return array_map( 'sanitize', $str ); return str_replace( "\0", '', $str ); } }

    • ベストアンサー
    • PHP
  • 再帰

    お世話になります。再帰でどう組んだらいいのかわからない問題があるので質問させてください 問題は、再帰を使ってlong型の整数に、3つずつの間隔でコンマを入れString型で返す演習問題です。 例) 999 → 999 1234 → 1,234 1007 → 1,007 1023004567 → 1,023,004,567 public static String intWithCommas(long n) { String strnum = String.valueOf(n); String str = ""; int length = strnum.length(); if (strnum.length() <= 3) { return strnum; } else { str = "," + strnum.substring(length-4, length-1); } return str + intWithCommas(?); } 上記のように組んだのですが、2箇所わからないところがあります。 カンマを入れるので整数を文字列に変換してsubstringで3つずつに分けてカンマを入れようと思ったのですが、 str = "," + strnum.substring(length-4, length-1); この部分をどのように直したらいいかがわかりません。 これではカンマが一番左に行ってしまうし、整数を3つずつにわけようにも、本来インデックスは左から数えますがこの場合は右から3つずつ数えなければなりません。 それと、最後の再帰を呼ぶところですが、intWithCommas(?);の中をどうしたらいいかがわかりません。 long型の整数を受け取ってるのでlong型の整数を入れるとおもうのですが、プログラムの中で整数をStringに変換してるのでどうしたらよいのでしょうか 宜しくお願いします。

    • ベストアンサー
    • Java
  • Array.newが必要ないのはなぜですか?

    以下のようなクラス拡張をしてみました。 Class String def count_word Array.new(ary) ary = self.split(/\s/) return ary.size end end ここで、Array.new()のところでエラーになってしまいます。 感覚的に配列で受け取るようにするべきだと思っていたの ですが、この1行を外すと正常に動きます。 なぜ配列を生成するのがまずいのでしょうか?

    • ベストアンサー
    • Ruby
  • 再帰プログラム

    #include<stdio.h> int rstrlen(char*); int main(void) { char str[100]; printf("文字列を入力してください\n"); gets(str); printf("文字数は %d です\n",rstrlen(str)); return 0; } int rstrlen(char *p) { if(*p){ p++; return 1+rstrlen(p); } else return 0; } 文字数を計算するプログラムです。 if(*p)の*pとはNULLを表しているのですか?

  • 多次元配列の処理について

    多次元配列を扱った処理を行ないたいのですが、一部でnullを受け取ってしまい、処理を行うことができません。 引数の配列には次のようなテキストが入っています。 --配列の内容-- 0,名詞-一般,1,5, 0,名詞-数,2,6, 0,名詞-接尾-助数詞,3,7, -------------- この配列をコマンドラインに書き出すと正確に表示されます。 しかし、配列の内容を参照して処理を行なうと2列目の処理のときにnullを参照してしまいます。 プログラムは以下のとおりです。 ---プログラム--- public class Dist {   public static int zairyo(String[][] date) {    int a = 0;    int b = 0;    int c = 0;    int d = 0;    int e = 0;    if(date != null) {     int f = 0;     System.out.println("//Dist//");     for(int m = 0; m < date.length; m++) {      for(int n = 0; n < date[0].length; n++) {       if(date[m][n] != null) {        System.out.print(date[m][n] + "\t");        f++;        if(f == date[0].length) {         System.out.println("");         f = 0;        }       }      }     }     for(int i = 0; i < date.length; i++) {      if(date[i][1] != null) {       if(date[i][1] == "名詞-一般")        a++;       if(date[i][1] == "名詞-数")        b++;       if(date[i][1] == "名詞-接尾-助数詞")        c++;      }else System.out.println("2列目null");     }     int j = date.length - 1;     d = Integer.parseInt(date[j][2]);     e = Integer.parseInt(date[j][3]);    }else System.out.println("Dist:null");    if(a > 0 && b > 0 && c > 0 && d <= 10 && e <= 15) {     return 1;    }else return 0;   } } ------------------------ 原因は何なのでしょうか? ご教授お願いします。

  • 再帰

    他の質問と平行してしまい申し訳ありません。再帰について少々おききしたいのですが、 配列の中にある数字の中から最大値と最小値を再帰処理でもとめたいのですが、うまくいきません。 public int minMax(n, array, min, max){ // nは配列のサイズです。 min=max=array[n-1]; if(array[n-2]<min) min=array[n-2]; if(array[n-2]>max) max=array[n-2]; return minMax(n-1,array,min,max); } 最初に比較するために min=max=array[n-1];と初期化したのですが、再帰処理ですからまた同じ初期化をしてしまうことになります。 forループなどを使えるなら初期化だけループの外でやれば済むのですが、再帰だとどのようにすればよいのでしょうか。 宜しくお願いいたします。

  • class内部でarray_mapをつかうとき、なぜこのような記述をしなければならないのでしょうか?

    PHP Version 5.1.6 です。 以下のようなクラスを書いていたのですが、 //----------------------------------------------------------- class PreventAttack{         public static function TopStaticMethod(){             self::ExcludeNullbyte();         }                  private static function ExcludeNullbyte(){             $_GET = self::ExcludeNullbyte_Exec( $_GET );             $_POST = self::ExcludeNullbyte_Exec( $_POST );             $_COOKIE = self::ExcludeNullbyte_Exec( $_COOKIE );             return;                  }             private static function ExcludeNullbyte_Exec($param){              if ( is_array( $param ) ) {              return array_map('ExcludeNullbyte_Exec', $param );              }              return str_replace( "\0", "", $param );             } } //----------------------------------------------------------- Warning: array_map() [function.array-map]: The first argument, 'ExcludeNullbyte_Exec', should be either NULL or a valid callback in /var/www/(中略)include/class/PreventAttack.class on line 39 と怒られてしまいました。 39行目は、下から5行目のarray_mapのところです。 で、このエラーメッセージでグーグルで検索して、いろいろたどっていったら、 http://jp.php.net/manual/ja/language.pseudo-types.php#language.types.callback にたどりつき、 このページのユーザコメント欄に、 //----------------------------------------------------------- To recap mr dot lilov at gmail dot com's comment: If you want to pass a function as an argument to another function, for example "array_map", do this: regular functions: <? array_map(intval, $array) ?> static functions in a class: <? array_map(array('MyClass', 'MyFunction'), $array) ?> functions from an object: <? array_map(array($this, 'MyFunction'), $array) ?> I hope this clarifies things a little bit //----------------------------------------------------------- と書いてあったので、 return array_map(array('ExcludeNullbyte_Exec', $param ); のところを、 return array_map(array('PreventAttack','ExcludeNullbyte_Exec'), $param ); に変えたら、エラーがなくなりました。 ・・・が、その理屈がいまいちわかりません。 どなたかおわかりの方がいらっしゃいましたら、お教えいただけないでしょうか? よろしくお願いいたします。

    • ベストアンサー
    • PHP
  • C++のSTL mapを使用するとセグメンテーション違反となる

    こんにちは。C++でSTLのmapの簡単な使用テストを以下のプログラムで行っており、動作確認ができました。 /* Assoc_array.c */ #define MAIN // (#include省略) using namespace std; namespace Usefuls { class Assoc_array_str { private: map<string, string> _h; public: void set(string key, string val); string get(string key); }; void Assoc_array_str::set(string key, string val){ _h.insert(pair<string, string>(key, val)); } string Assoc_array_str::get(string key){ map<string, string>::iterator p; p = _h.find(key); return p->second; } } #ifdef MAIN int main(){ Usefuls::Assoc_array_str assoc; assoc.set("Konnichiwa", "Hello"); cout << assoc.get("Konnichiwa") << endl; return 0; } #endif /* ここまで */ しかし、これを以下のように他のファイルから呼び出すと(先頭の#define MAINを#undef MAINに変えます)、「セグメンテーション違反です」というメッセージが出てしまいます。 /* Aa_test1.c */ #include <iostream> using namespace std; namespace Usefuls { class Assoc_array_str { public: void set(string, string); string get(string); }; } int main(){ Usefuls::Assoc_array_str assoc; assoc.set("Konnichiwa", "Hello"); cout << assoc.get("Konnichiwa") << endl; return 0; } /* ここまで */ コンパイル方法は以下の通りです。 g++ -c Assoc_array.c g++ Aa_test1.c Assoc_array.o また、使用プラットホームはLinuxのCentOS 4.3です。 原因が分かる方、ご回答をよろしくお願いします。

  • 文字列の比較

    いつも参考にさせて頂いています。 基本的なことなのですが、宜しくお願いします。 たとえば文字列"str"の空チェックをするとします。 -------------------以下参考ソース String str = "test"; 1. public static String isBlank(String str) { if(str == null || str.equals("")) { return true; } return false; } 2. public static String isBlank(String str) { if(str == null || str.length = 0) { return true; } return false; } ■1と2の違いって何かありますでしょうか? ■空チェックなら1と2のどちらを使うべきでしょうか? 以上宜しくお願いします。

    • ベストアンサー
    • Java
  • 漢字を配列に入れたいのですが

    漢字を配列に入れたいのですが、うまくいきません。 3列、60行のcsvファイルを読み込んで配列に入れようをしているのですが、1列目、2列目、3列目にある漢字をそれぞれ配列に入れようとしているのですが、出力するとうまくいかないんです。誰か教えてください。 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXBUFFSIZE 256 #define MAXWORDS 15 int split(char* words[], int length, char* split_ch,char* str){ int i,j; for(i=0;i<length;i++){ if((words[i] = strtok(str,split_ch))==NULL)break; str=NULL; } return(i); } int main(int argc, char* argv[]){ if(argc !=2){ printf("入力エラー"); return(0); } FILE *fp; char *ll, *words[MAXWORDS], ch, buff[MAXBUFFSIZE]; int i,j; unsigned int data1[60], data2[60], data3[60]; if((fp =fopen(argv[1],"r"))==NULL){ printf("ファイルが開けません。\n"); } j=0; ll= fgets(buff,MAXBUFFSIZE,fp); while((ll= fgets(buff,MAXBUFFSIZE,fp)) != NULL){ split(words, MAXWORDS, ",",ll); data1[j] = words[0]; data2[j] = words[1]; data3[j] = words[2]; j++; } printf("%s\n%s\n%s\n", data1,data2,data3); }