14
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CoffeeScriptで無名関数の再帰

Last updated at Posted at 2015-05-25

#当たり前っちゃあ当たり前なのだが
なのだが! とりあえず、Javascriptについて書かれていて、CoffeeScriptだと探すのにちょっと苦労するTIPSは、遠慮なくポストします。

とりあえず、無名関数の再帰呼び出しに関してのよくある悩み事はこんな感じかな。

  • arguments.calleeだったっけ? 存在がちょっとヤバゲだし、なくなるんじゃなかったっけ?
  • Yコンビネータとかなにそれおいしーの? なんか怖いんだけど。
  • 1引数関数の例があちこちにあるけど、2引数渡したいんだけど駄目?
    まあ、3番目のは、「引数をオブジェクトに纏めてどれもこれも同じYコンビネータ使うのも良いかもね」とゆー考えもなくもないのですが、関数を引数に取る関数に関数を引数に取る関数自身を渡す関数を使う、っていうコンセプトなんだから、複数の引数だってできるに決まってんじゃん、とか、やっている人は思うんだけど、多分怖い人は怖い。それにね、1変数関数を再帰にする例って、Yコンビネータの原理が分かりにくいと思うんだよね。Yコンビネータの仕組みをカラダで感じないというか。でもな、おじさんにぜんぶまかせてごらん、きもちーよ?

でもって、augumentsという変な変数とapplyという変な関数呼び出しの仕方をすれば、いくつでも変数が渡せます。それは、きもいと感じる人にはきもいので、また別項改めて。

#では、サンプルです
折角、CoffeeScriptを使うと、Yコンビネータの見た目もちょっと優しく愛らしい感じになるので、サンプル行っときましょう。題材は、身近な再帰ということで、しかも、引数が色々増えそうな感じなのはディレクトリ走査ですかね。

ycombi.coffee
fs = require('fs')

scandir_inside = (func) ->
  return (srcpath, param) ->
    if not fs.existsSync srcpath
      return -1
    filearray = fs.readdirSync srcpath
    for fileordir in filearray
      statresult = fs.statSync srcpath+'/'+fileordir
      if statresult.isFile()
        console.log 'scanfiles '+fileordir
      else if statresult.isDirectory()
        console.log 'scandirs '+fileordir
        func srcpath+'/'+fileordir,param


#所謂Yコンビネータ
y_with_2_args = (func) ->
  return ((p) ->
    return (q,r) ->
      return func(p(p))(q,r)
  )((p) ->
    return (q,r) ->
      return func(p(p))(q,r)
  )

#Yコンビネータを噛ます
scandir = y_with_2_args(scandir_inside)

#ためしてみようほととぎす
scandir '/path/to/start/directory',{project: 'testtest', test:12}

scandir_insideの最後の行で、funcを呼んでいますね。これは、1行目の引数のfuncです。つまり、渡される関数を呼び出すクロージャになっているだけで、これだけでは必ずしも再帰とは限らない書き方なわけですが、まあ、再帰しようと思って書いてますよね。どうみても。

で、Yコンビネータが、「引数(である関数)を、引数(である関数)を引数として呼び出す」ことによって、再帰を実現します。入口と出口があるパイプ(scandir_inside)に入口と出口を繋げる装置(y_with_2_args)を嵌めこんだら、循環する機械ができました(scandir)って感じ。

ここでのYコンビネータ(別名不動点コンビネータ)は、2引数用。3引数にしたければ、4箇所全部q,r,sにして下さい。「引数(である関数)を、引数(である関数)を引数として呼び出す」ので、その「引数(である関数)」の呼び出しの形は合わせないと使えない、ってことです。

さて、追記しておきましょう。さっきのは原理を理解しやすくするためにscandir_insideなんて変数に入れたけど、もう分かりますよね。直接Yコンビネータの引数の中に無名関数を入れちゃえば、そんなの要らなくなると言うことを!

なので、実践的にはこんな風に書くとカコイイ!です。

ycombi.coffee
fs = require('fs')

#所謂Yコンビネータ
y_with_2_args = (func) ->
  return ((p) ->
    return (q,r) ->
      return func(p(p))(q,r)
  )((p) ->
    return (q,r) ->
      return func(p(p))(q,r)
  )

#Yコンビネータを噛ましつつ無名関数を書いちゃう
scandir = y_with_2_args (func) ->
  return (srcpath, param) ->
    if not fs.existsSync srcpath
      return -1
    filearray = fs.readdirSync srcpath
    for fileordir in filearray
      statresult = fs.statSync srcpath+'/'+fileordir
      if statresult.isFile()
        console.log 'scanfiles '+fileordir
      else if statresult.isDirectory()
        console.log 'scandirs '+fileordir
        func srcpath+'/'+fileordir,param

#ためしてみようほととぎす
scandir '/path/to/start/directory',{project: 'testtest', test:12}

使えばだんだん気持ちよくなるけど、別に奥は深くないTIPSです。ご参考まで。

14
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?