読者です 読者をやめる 読者になる 読者になる

CyanでCLのfind系関数っぽいものを生成するマクロ

定義に使うだけなのでselfの退避先以外の(必要なかった)シンボルは決め打ち。捕捉とかも大丈夫、だと思うけどどうだろう?
引数の取り方もアレだけど定義に使うだけなので以下略

def(Object.equal)^(obj):
  (.parent == obj.parent) && (self == obj)

mac(make_finder)^(name, cond, result, &opt acc0, acc_next, fin):
  func := Function.new($(arg),`{
    let(rec)^(&opt self = self, acc = ?acc0):
      if (.null?()):
        ?fin
       else:
        if (?cond):
          ?result
         else:
          rec(self.cdr(), ?acc_next)
  })
  Porter.new('def, &(Messenger.new('List, name), func))

make_finder(find, arg.equal(.car()), .car())  
make_finder(find_if, arg(.car()), .car())  
make_finder(member, arg.equal(.car()), self)  
make_finder(member_if, arg(.car()), self)  
make_finder(position, arg.equal(.car()), acc, 0, acc + 1)
make_finder(position_if, arg(.car()), acc, 0, acc + 1)  
make_finder(remove, arg.equal(.car()), rec(.cdr(), acc), [], acc.append([.car()]), acc)
make_finder(remove_if, arg(.car()),rec(.cdr(),acc), [], acc.append([.car()]), acc)
make_finder(some, arg(.car()), true, [], [], false)
make_finder(every, !arg(.car()), false, [], [], true)
make_finder(notevery, !arg(.car()), true, [], [], false)
make_finder(notany, arg(.car()), false, [], [], true)
#|
def(List.every)^(f): !.some(complement(f))
def(List.notevery)^(f): .some(complement(f))
def(List.notany)^(f): !.some(f)
|#

mac(_?)^(exp):
  say "#?= " + exp.to_s()
  `let^(&opt result = (?exp)):
    say "#?- " + result.to_s()
    result

def(findtest)^:
  x := iota(10)
  _? x
  _? x.find(4)
  _? x.find_if^(n): n > 8
  _? x.member(5)
  _? x.member_if^(n){ n * n > 10 }
  _? x.position(6)
  _? x.position_if^(n){n.rem(2) == 1}
  [^(n){ n < 10}, ^(n){ n < 0 }, ^(n){ n.rem(2) == 0 }].foreach^(f):
    say([f, x.some(f), x.every(f), x.notevery(f), x.notany(f)].to_s())
  _? x.remove(5)
  _? x.remove_if^(n){ n.rem(3) == 0 }

named let便利。CLではpregexpのソースにあったrecurっていうマクロをちょこちょこ使ってたりしたんだけれども。

てすてす

cyan> findtest()
#?= x
#?- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#?= x.find(4)
#?- 4
#?= x.find_if(^(n){ n.(>)(8) })
#?- 9
#?= x.member(5)
#?- [5, 6, 7, 8, 9]
#?= x.member_if(^(n){ n.(*)(n).(>)(10) })
#?- [4, 5, 6, 7, 8, 9]
#?= x.position(6)
#?- 6
#?= x.position_if(^(n){ n.rem(2).(==)(1) })
#?- 1
[^(n){ n.(<)(10) }, true, true, false, false]
[^(n){ n.(<)(0) }, false, false, true, true]
[^(n){ n.rem(2).(==)(0) }, true, false, true, false]
#?= x.remove(5)
#?- [0, 1, 2, 3, 4, 6, 7, 8, 9]
#?= x.remove_if(^(n){ n.rem(3).(==)(0) })
#?- [1, 2, 4, 5, 7, 8]
 => [1, 2, 4, 5, 7, 8]

ネタ

cyan> make_finder(len, [], [], arg, acc + 1, acc)
 => ^(self, arg){ let(rec, ^(&opt self = self, acc = arg){ if(self.null?(), { acc }, :else { if([], { [] }, :else { rec(self.cdr(), acc.(+)(1)) }) }) }) }
cyan> [1,2,3].len(0)
 => 3
cyan> make_finder(rev, [], [], arg, [.car() | acc], acc)
 => ^(self, arg){ let(rec, ^(&opt self = self, acc = arg){ if(self.null?(), { acc }, :else { if([], { [] }, :else { rec(self.cdr(), [self.car() | acc]) }) }) }) }
cyan> [1,2,3].rev([])
 => [3, 2, 1]


ついでにmap系。

mac(make_mapper)^(name, args_form, acc_next):
  func := Function.new($(fun), `{
    let(rec)^(&opt self = self, acc):
      args := ?args_form
      if (.some^(_){_.null?()}):
        acc
       else:
        rec(.map^(_){_.cdr()}, ?acc_next)
  })
  Porter.new('def, &(Messenger.new('List, name), func))

make_mapper(mapc, .map^(a){ a.car() }, fun(*args))
make_mapper(mapcar, .map^(a){ a.car() }, acc.append([fun(*args)]))
make_mapper(mapcan, .map^(a){ a.car() }, acc.append(fun(*args)))
make_mapper(mapl, self, fun(*args))
make_mapper(maplist, self, acc.append([fun(*args)]))
make_mapper(mapcon, self, acc.append(fun(*args)))

def(maptest)^:
  x := [iota(5), iota(5,5)]
  _? x.mapc^(a,b): say(a * b)
  _? x.mapcar^(a,b): [a, b]
  _? x.mapcan^(a,b): [a, b]
  _? x.mapl^(a,b): say(a.append(b).foldl(0)^(c,d){ c + d })
  _? x.maplist^(a,b): a.append(b)
  _? x.mapcon^(a,b): a.append(b)

てst

cyan> maptest()
#?= x.mapc(^(a, b){ say(a.(*)(b)) })
0
6
14
24
36
#?- 36
#?= x.mapcar(^(a, b){ [a, b] })
#?- [[0, 5], [1, 6], [2, 7], [3, 8], [4, 9]]
#?= x.mapcan(^(a, b){ [a, b] })
#?- [0, 5, 1, 6, 2, 7, 3, 8, 4, 9]
#?= x.mapl(^(a, b){ say(a.append(b).foldl(0, ^(c, d){ c.(+)(d) })) })
45
40
33
24
13
#?- 13
#?= x.maplist(^(a, b){ a.append(b) })
#?- [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 6, 7, 8, 9], [2, 3, 4, 7, 8, 9], [3, 4, 8, 9], [4, 9]]
#?= x.mapcon(^(a, b){ a.append(b) })
#?- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 6, 7, 8, 9, 2, 3, 4, 7, 8, 9, 3, 4, 8, 9, 4, 9]
 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 6, 7, 8, 9, 2, 3, 4, 7, 8, 9, 3, 4, 8, 9, 4, 9]

mapc, maplは元のリストを返すのではなく、mapfっぽく最後の結果を返すようにしてみた。
mapfはまたいつか。