「」に属する記事(最新5件のみ展開表示)

メイン

2006年09月01日

Just Another Ruby/Python Hacker,

実行すると

Just Another Ruby/Python Hacker,

と表示されます。cygwinで確認しました。

なお、このRubyワンライナーを作るにあたってRubyのある風景 - rrencodeのてくにかるいしゅーが非常に参考になりました。というかRubyは詳しくないので自力では無理でした。

なお「どのへんがPythonなんだ」と思う方は実行されたコマンドの最後と下の「このRubyスクリプトを作るのに使ったPythonスクリプト」の最初の方を読むとわかるかも知れません。


__ 使う記号の種類を限定してさらにひどいものにしてみました。「<<」を文字列の結合とシフト演算の両方に使ったり、「?)-?(」(イコール1)を多用するようにしたり、という嫌らしいことをしてあります。?(や?)のせいで、ただでさえ括弧を頼りにして読むことが難しいのに、括弧を必要以上につけてあるのでさらに読みづらくなっています。でもなんかきれい。

ruby -e "_=%?%?<<?!+?!+?!;$><<_%((((?)-?())+(?%+(?%<<?)-?())))<<_%((?%+(?)+?+)))
<<_%(((?%+(?+<<?(-?)))<<?)-?())<<_%((?%+(?-+(?-<<?(-?)))))<<_%((?%+(?%<<?)-?()))
<<_%(((?%+(?%<<?(-?)))<<?)-?())<<_%((?%-(?--?()))<<_%(?-)<<_%((?%+(?(+(?-<<?(-?)
))))<<_%((?%-(?--?()))<<_%((?%-(?(-?%)))<<_%((((?)-?())+(?%+(?%<<?)-?())))<<_%((
?%+(?%+?()))<<_%((?(+(?++(?-<<?(-?)))))<<_%(((?%+(?%<<?(-?)))<<?)-?())<<_%(((?%+
(?+<<?(-?)))<<?)-?())<<_%((?%-(?--?()))<<_%((?(-((?)-?())))<<_%((?%<<?)-?())<<_%
((?%+(?(<<?)-?()))<<_%((?%+(?%+?))))<<_%(((?%+(?+<<?(-?)))<<?)-?())<<_%((?%-(?--
?()))<<_%((?++(?-<<?(-?))))<<_%(((?%+(?%<<?(-?)))<<?)-?())<<_%((?%+(?%<<?)-?()))
<<_%(((?%+(?+<<?(-?)))<<?)-?())<<_%((?%+(?-+(?-<<?(-?)))))<<_%((?%+(?++(?+<<?(-?
)))))<<_%((?%+(?%+?()))<<_%((?%-(?--?()))<<_%((?)<<?)-?())<<_%((?%+(?(<<?)-?()))
<<_%(((?)+(?--?%))<<?)-?())<<_%((?%+(?)+?+)))<<_%((((?)-?())+(((?)-?())+?-)))<<_
%((?(<<?)-?())<<_%((?%+(?)+?+)))<<_%(((?%+(?+<<?(-?)))<<?)-?())<<_%((?%+(?-+(?-<
<?(-?)))))<<_%((?%+(?%<<?)-?()))<<_%(((?%+(?%<<?(-?)))<<?)-?())<<_%((?%-(?--?())
)<<_%(((?%-((?)-?()))<<?)-?())<<_%((?%+(?(+(?(<<?(-?)))))<<_%((?%+(?(+(?-<<?(-?)
))))<<_%((?(+(?-+(?-<<?(-?)))))<<_%((?%+(?++(?+<<?(-?)))))<<_%((?%+(?%+?()))<<_%
((((?)-?())+?+))<<_%((?(-((?)-?())))<<_%((?%-(?(-?%)))" | sh

シンプルに「Just Another Ruby Hacker,」と表示するスクリプトは下のようになります。

ruby -e "_=%?%?<<?!+?!+?!;$><<_%((?%<<?)-?())<<_%((?%+(?(<<?)-?()))<<_%((?%+(?%+
?))))<<_%(((?%+(?+<<?(-?)))<<?)-?())<<_%((?%-(?--?()))<<_%((?++(?-<<?(-?))))<<_%
(((?%+(?%<<?(-?)))<<?)-?())<<_%((?%+(?%<<?)-?()))<<_%(((?%+(?+<<?(-?)))<<?)-?())
<<_%((?%+(?-+(?-<<?(-?)))))<<_%((?%+(?++(?+<<?(-?)))))<<_%((?%+(?%+?()))<<_%((?%
-(?--?()))<<_%((?)<<?)-?())<<_%((?%+(?(<<?)-?()))<<_%(((?)+(?--?%))<<?)-?())<<_%
((?%+(?)+?+)))<<_%((?%-(?--?()))<<_%(((?%-((?)-?()))<<?)-?())<<_%((?%+(?(+(?(<<?
(-?)))))<<_%((?%+(?(+(?-<<?(-?)))))<<_%((?(+(?-+(?-<<?(-?)))))<<_%((?%+(?++(?+<<
?(-?)))))<<_%((?%+(?%+?()))<<_%((((?)-?())+?+))"

これらのコードを出力したPythonスクリプトは下記。

続きを読む "Just Another Ruby/Python Hacker," »

2006年07月27日

PythonとRubyでパスカルの三角形ワンライナー

rubyco(るびこ)の日記 - 呼び出し側のアスタリスクを読んで、「あ、そうか、それを使えば頭から二つずつ取ることができるか」とGame Scripting Memo - 続・パスカルの三角形のことを思い出しました。

>>> def get2(f, a, b, *rest):
	result = [f(a, b)]
	if rest:
		result += get2(f, *rest)
	return  result

>>> from operator import add
>>> def dup(aList):
	return reduce(add, [[v, v] for v in aList])

>>> dup([1,2,3])
[1, 1, 2, 2, 3, 3]
>>> def sandwich(bread, meat):
	return [bread] + meat + [bread]

>>> sandwich(0, dup([1, 2, 1]))
[0, 1, 1, 2, 2, 1, 1, 0]

>>> get2(add, *sandwich(0, dup([1, 2, 1])))
[1, 3, 3, 1]

とりあえずワンライナーにするために手を加えて…

>>> def get2(f, a, b, *rest):
	return rest and [f(a, b)] + get2apply(f, *rest) or [f(a, b)]

>>> def next(xs):
	return get2(add, *sandwich(0, dup(xs)))

>>> def next(xs):
	return get2(add, *sandwich(0, reduce(add, [[v, v] for v in xs])))

>>> def next(xs):
	return get2(add, *[0] + reduce(add, [[v, v] for v in xs]) + [0])

>>> next([1])
[1, 1]
>>> next(_)
[1, 2, 1]
>>> next(_)
[1, 3, 3, 1]
>>> next(_)
[1, 4, 6, 4, 1]
>>> def next(xs):
	g = lambda f, a, b, *rest: rest and [f(a, b)] + g(f, *rest) or [f(a, b)]
	return g(add, *[0] + reduce(add, [[v, v] for v in xs]) + [0])

>>> def next(xs): g = lambda f, a, b, *rest: rest and [f(a, b)] + g(f, *rest) or [f(a, b)]; return
g(add, *[0] + reduce(add, [[v, v] for v in xs]) + [0])

>>> next([1])
[1, 1]

いちおうワンライナーになりました。

>>> lambda xs: (lambda g: (lambda *args: g(g, *args)))(lambda g, f, a, b, *rest: rest and [f(a, b)] +
 g(g, f, *rest) or [f(a, b)])(add, *[0] + reduce(add, [[v, v] for v in xs]) + [0])
<function <lambda> at 0x0146BC70>
>>> _([1, 2, 1])
[1, 3, 3, 1]

これで文を使わない1つの式になりました。


__ Rubyは詳しくないので…ええっと、Proc.new{|x| x + 1}で無名関数のようなものができて、callでそれを呼べるんですかね。あ、 proc {...} で Proc.new{...} と同じだそうです。


$g = proc {|a, b, *rest|
	if rest.size == 1
		[a + b, rest[0]]
	else
		[a + b] + $g.call(*rest)
	end
}


(0..9).inject([1]){|xs, item|
	proc{|x| p x; x}.call(
		$g.call(0, *xs.map{|v| [v, v]}.flatten)
	)
}

三角形の頭が表示されないですけど一応動きました。


__

(0..9).inject([]){|xs, i|
  proc{|x| p x; x}.call(
    if i == 0
      [1]
    else
      proc{|xs|
        proc {|g|
          proc {|*args|
            g.call(g, *args)
          }
        }.call(
          proc {|g, a, b, *rest|
            if rest.size == 1
              [a + b, rest[0]]
            else
              [a + b] + g.call(g, *rest)
            end
          }
        ).call(
          0, *xs.map{|v| [v, v]}.flatten
        )
      }.call(xs)
    end
  )
}

できました。改行を取り除くのはお任せします。

PythonとRubyでデフォルト引数の評価されるタイミングは違う

驚きました。 rubyco(るびこ)の日記 - デフォルト引数は明示的に評価されると同じことをPythonで書いた場合、関数定義の時点で評価されます。

>>> def default(n):
	print "default", n
	return n + 1

>>> def foo(x, y = default(5)):
	print x, y

	
default 5

Rubyでは関数が呼ばれた時にデフォルト引数を評価するようです。しかも遅延評価とかではないようで、何度も関数を呼べば何度も実行されます。つまりPythonで書けば

>>> def foo(x, y = None):
	if y == None: y = default(5)
	print x, y

に相当する状態のようです。

2006年07月05日

RubyのyieldとPythonのyieldの違い

RubyのyieldとPythonのyieldは実は全然違うような気がしてきたので、整理のために書いてみます。 まず、Rubyでeachに相当する物を自分で実装してみました。

# Ruby 1
class Array
  def my_each
    self.each{|x|
      yield x
    }
  end
end

[1, 2, 3, 4, 5].my_each{|x|
  puts x
}

これに相当する物をPythonで書くと、実はyieldを使いません。

# Python 1
class Array(list):
    def each(self, f):
        for i in self:
            f(i)

def myprint(x):
    print x

Array([1, 2, 3, 4, 5]).each(
    myprint
)

これをもっとRuby 1のコードに近づけて書くと以下のようになります。Pythonにはputsに相当する関数がsys.stdout.writeという長ったらしい物なのでputsは自分で定義していますがそこは本質ではありません。重要なのは「引数xを取ってputs(x)を評価せよ」というモノ(Python流に言うと無名関数、Ruby流に言うとブロック)を渡しているということだと思います。

# Python 2
class Array(list):
    def each(self, f):
        for i in self:
            f(i)

def puts(x):
    print x

Array([1, 2, 3, 4, 5]).each(lambda x:
    puts(x)
)

逆にRuby 1の方をPython 1に近づけて書くと次のようになるようです。

# Ruby 2
class Array
  def my_each
    self.each{|x|
      yield x
    }
  end
end

myprint = proc {|x|
  puts x
}

[1, 2, 3, 4, 5].my_each(
  &myprint
)

Python 1 では関数オブジェクトを引数に渡していましたが、Ruby 2ではProcオブジェクトを渡すようです。

さて、ではPythonでyieldを使うとどうなるでしょう。

# Python 3
class Array(list):
    def each(self):
        for i in self:
            yield i

for i in Array([1, 2, 3, 4, 5]).each():
    print i

Rubyと大きく異なる点は、forが必要である点と、eachが引数を取らない点です。Rubyの「yieldを使っているメソッド」は「Procオブジェクトを受け取ってyieldの場所でyieldの後に続いているモノを引数としてそのProcオブジェクトを呼び出す」という働きでしたが、Pythonの「yieldを使っているメソッド」は「なにも受け取らず、ジェネレータを返す」という働きをします。ジェネレータはnextメソッドを持ち、nextが呼ばれるとyieldまでを実行してyieldの後に続いているモノを返り値として返します。ですので、ループを途中で中断することもできますし、中断したループを後で継続することもできます。

# Python 4
class Array(list):
    def each(self):
        for i in self:
            yield i

gen = Array([1, 2, 3, 4, 5]).each()
for i in gen:
    print i
    if i == 3:
        break

print gen.next() # -> 4

Rubyで同じことをやろうとすると、明示的にジェネレータを使って以下のようになります。

# Ruby 3
require 'generator'

g = Generator.new([1, 2, 3, 4, 5])

while g.next?
  i = g.next
  puts i
  if i == 3
    break
  end
end

puts g.next

Generator.newの引数にはProcオブジェクトを渡せるようですので、無限リストはこんな感じに実装できますね。

# Ruby 4
require 'generator'

def makeList
  Generator.new{|g|
    i = 0
    while true 
      i += 1
      g.yield i
    end
  }
end

g = makeList

while g.next?
  i = g.next
  puts i
  if i == 3
    break
  end
end

puts g.next

PythonでRuby 4に相当するコードを書くとこうです。

# Python 5
def makeList():
    i = 0
    while True:
        i += 1
        yield i

gen = makeList()
for i in gen:
    print i
    if i == 3:
        break

print gen.next() # -> 4

Rubyでは引数gにProcオブジェクト?を受け取っているようですね。Pythonではジェネレータの時には省略できて、Ruby風のループ抽象(イテレータ?)を行うときには省略せずに引数fに関数オブジェクトを受け取っていました。Rubyではちょうど逆ですね。面白いです。

古い記事タイトル一覧

凡例{ ●: 単一エントリーへのリンク, □: そこから最新記事までを一覧表示, ■: そこから最新記事までをwindow.openで開く}(comming soon)