無名関数(PHP, Python, Ruby)

<?php
function divGeneric($dir, $f, $arg){
    if(!is_dir($dir)) return false;

    if($hd = opendir($dir)){
        while(false !== $file = readdir($hd)){
            if($file != "." && $file != ".."){
                $f($dir,$file,$arg,$f);
            }
        }
        closedir($hd);
    }

    return $arg;
}
?>


↑のように指定したディレクトリ以下のエントリを走査しながら、
各エントリに対して、引数で渡された関数を実行する関数をよく使うのだけど、
これと同じようなのをPythonとかRubyでも書いてみた。
以下は、指定したディレクトリ以下のエントリを再帰的に走査しながら、
配列にエントリを追加していくプログラム。

PHP

<?php

function divGeneric($dir, $f, $arg){
    if(!is_dir($dir)) return false;

    if($hd = opendir($dir)){
        while(false !== $file = readdir($hd)){
            if($file != "." && $file != ".."){
                $f($dir,$file,$arg,$f);
            }
        }
        closedir($hd);
    }

    return $arg;
}

$f = create_function('$dir,$file,&$array,$f', 
                     'if(is_dir($dir."/".$file)){
                          $array[] = $dir;
                          $array = divGeneric($dir."/".$file, $f, $array);
                      }
                      else{
                          $array[] = $dir."/".$file;
                      }
                     ');

$ret = divGeneric("/home/bokko/music", $f, array());

var_dump($ret);

?>

Python

#!/usr/bin/python
# -*- encoding: utf-8  -*-

import os.path;

def divGeneric(dir, f, arg):
    if not os.path.isdir(dir):
        return False
    
    entryList = os.listdir(dir)
    for entry in entryList:
        f(dir, entry, arg)

    return arg;
    
def divDirOrFile(dir, entry, dir_f, file_f, arg, func):
    if os.path.isdir(dir + "/" + entry):
        dir_f(dir, entry, arg)
        arg = divGeneric(dir + "/" + entry, func, arg) 
    else:
        file_f(dir, entry, arg)

    return arg

func = lambda dir,entry,arg : divDirOrFile(dir, entry,
                                           lambda dir,entry,arg : arg.append(dir),
                                           lambda dir,entry,arg : arg.append(entry),
                                           arg, func)

arg = divGeneric("/home/bokko/music", func, [])

print arg

Ruby

#!/usr/bin/ruby
$KCODE = 'u'

def divGeneric(dir, f, arg)
    
  if not File.directory?(dir)
    return false
  end
  
  entries = Dir::entries(dir)
  entries.delete_at(0)
  entries.delete_at(0)
  for entry in entries
    f.call(dir,entry,arg,f)
  end
  
  return arg
end

f = lambda{|dir,file,array,f| 
  if File.directory?(dir + "/" + file) 
    array << dir
    array = divGeneric(dir + "/" + file, f, array)
  else
    array << dir + "/" + file
  end
  return array
}


ret = divGeneric("/home/bokko/music", f, [])

p ret;


書いてて思ったけど、PHPだと無名関数はcreate_functionに文字列で渡さないといけないし、
Pythonだとlambda式には単一の式しか書けないけど、Rubyの場合はそんなこと気にしないで書ける。


ただ、前にこんなことを書いて、

そもそも、関数を引数にするということは、静的に流れが読めなくなるので、
上流工程とか、形式的なプログラム検証からすると、非常に困る。

というふうに、ある人からお叱りを受けたことを考えると、あんまり長ったらしい処理を
無名関数として引数で渡したりしない方がいいのかもしれない。
そう考えると、Pythonがlambda式に単一の式しか渡せないのは、技術的な問題というよりは、
可読性とかを重視した結果なのかな。



P.S.


現在、また東京にいます。GWの最終日に東京行きの新幹線なんて乗るもんじゃないですね(_ _)。