Tomcatハック日記2 JSPの章
<%@ page
contentType="text/html; charset=Shift_JIS"
session = "false"
import = "org.python.util.PythonInterpreter"
%>
<html>
<body>
<pre>
<%
PythonInterpreter pyi = new PythonInterpreter();
String code = request.getParameter("code");
pyi.set("out", out);
pyi.exec("import sys; old_out = sys.stdout; sys.stdout = out");
if(code == null){
code = "";
}
pyi.exec(code);
pyi.exec("sys.stdout = old_out");
%>
</pre>
<form method="post" action="test.jsp">
<textarea name="code" cols=80 rows=10><%= code %></textarea><br>
<input type="submit">
</form>
</body>
</html>
テキストエリアに「print "hoge" * 3」とか書いてsubmitボタンを押すと、
ちゃんと「hogehogehoge」と表示される。
でもこれだとPythonインタプリタが毎回死んでしまう。
どうしたらいいのかな。
とりあえずsession.setAttributeして使い回すようにしてみた。 セッションが続いている間は同じインタプリタを使うようにした。 import osしたら、os.listdirでローカルのファイル一覧が見える。 特にセキュリティ上の制約とかはないみたいだ。
「print session.getValueNames()」で 「array(['pyi', 'hoge'], java.lang.String)」と表示された。 「session.setAttribute("hoge", 2)」で問題なく書き換えられた。
とりあえずsessionの中身を自由に書き換えられるようにはなった。
アプリケーションスコープの変数を使えば 他のセッションとPythonインタプリタを共有できそうだ。 Firefoxの画面で「x = 1」を実行してから、IEを起動して「print x」とやったら、 無事「1」と表示された。
あとは何ができるとうれしいんだろう。 見かけを対話型インタプリタに近づけるのは、技術的には難しくないと思うしな。 チャット風に。
<%@ page
contentType="text/html; charset=Shift_JIS"
import = "org.python.util.PythonInterpreter"
%>
<html>
<body>
<textarea cols=80 rows=10>
<%
PythonInterpreter pyi = (PythonInterpreter)application.getAttribute("pyi");
if(pyi == null){
pyi = new PythonInterpreter();
application.setAttribute("pyi", pyi);
}
String code = request.getParameter("code");
pyi.set("session", session);
pyi.set("out", out);
pyi.exec("import sys; old_out = sys.stdout; sys.stdout = out");
if(code == null){
code = "";
}
pyi.exec(code);
pyi.exec("sys.stdout = old_out");
%>
</textarea>
<form method="post" action="test.jsp">
<textarea name="code" cols=80 rows=10><%= code %></textarea><br>
<input type="submit">
</form>
</body>
</html>
= 雨蛙散乱前線。
= JythonインタプリタからJyConsoleを起動する。 昨日JythonインタプリタからTomcatを起動したのと同じ方法で起動まではうまく行く。 でも、Jythonインタプリタ側の入出力が動かなくなる。
よく考えたら普通にJavaプログラムでJyConsoleを立ち上げた上で、 そのJyConsoleの持っているPythonインタプリタに、 そのPythonインタプリタへの参照をつっこんでやればいい。 自分自身を動的に書き換えられるインタプリタになる。
package org.nishiohirokazu.jythonbook.test;
import javax.swing.JFrame;
import com.artenum.jyconsole.JyConsole;
import com.artenum.jyconsole.python.JInteractiveInterpreter;
public class Ouroboros {
public static void main(String[] args) {
System.setProperty("python.security.respectJavaAccessibility", "false");
JyConsole jyc = new JyConsole();
JInteractiveInterpreter pyi = jyc.getPythonInterpreter();
pyi.set("pyi", pyi);
JFrame f = new JFrame();
f.setSize(500, 300);
f.add(jyc);
f.setVisible(true);
}
}
= 今日は研究室の今年度最後の意見交換会。 ある種の忘年会。忘年度会。
そんなわけでまとまった執筆はできないと踏んでいたので色々試してみた↑
あととりあえずGHC(Haskell)をインストールしてみた。 missingPyを試したい。
ghci(対話的インタプリタ)で関数が定義できないって、ひどい。
HelloWorldできた。 mainという変数の値はアクションというものらしい。 ふむふむ。 たぶん純粋な関数のことだけを関数と呼んで、 副作用のある関数はアクションと呼ぶんだろう。
main = do x <- "Hello. world"
putStrLn x
怒られた。
main = putStrLn x where x = "Hello. world"こうか。
main = let x = "Hello. world"
in putStrLn x
これもOK。
関数適用は f arg1 arg2 arg3で Java風にいうとf(arg1, arg2, arg3)になるわけだけど、 これって部分適用があるから f(arg1)(arg2)(arg3)なんだよね?
Lispと比べて括弧が少ないけど、 その代わりに$があるよ!
階乗。
main = print $ facto 5 facto :: Int -> Int facto 1 = 1 facto n = n * facto (n - 1)パターンマッチで関数を定義するところがPrologっぽくて面白い。 あと、最初「main = print facto 5」と書いて怒られた。 factoにprintを適用してしまうんだな、きっと。 関数適用が左結合性ということ? 「facto (n - 1)」も最初 「facto n - 1」と書いて、スタックオーバーフロー。 「facto(n) - 1」と認識されたんだろう。
LispやSchemeと違って括弧が省略できて楽だけど、 その分、どこがどう結合するのかは意識する必要がありそう。
「facto $ n - 1」にすると怒られる。 たぶん n を関数だと思ったんだろう。 「facto & - n 1」にしても怒られる。 たぶん - は関数ではないんだろう。
あ、違う。 「facto $ n - 1」にすると怒られるのは 「(n * facto) $ (n - 1)」と解釈されて、 IntのnとInt->Intのfactoの掛け算が定義されていないからだ。
facto n = (\x -> n * x) $ facto $ n - 1
これならちゃんと動く。括弧なしで書こうと思ったんだけど、とっちゃうとダメ。
carがheadでcdrがtail。
=
main = print $ minMap [3, 2, 5, 1] minMap :: [Int] -> Int minMap [x] = x minMap (x:xs) = min x $ minMap xsふむふむ。
= 100までの素数。
main = print $ filter isPrime [2..100] isPrime :: Int -> Bool isPrime x = not $ any (\n -> n == 0) $ map (mod x) [2..x-1]簡単。「(\n -> n == 0)」はなんかいい書き方ないのかなあ。
= ガードを使ってみた。Collatzの数列。
main = print $ collatzList 7
collatz :: Int -> Int
collatz x | mod x 2 == 0 = div x 2
| otherwise = x * 3 + 1
collatzList :: Int -> [Int]
collatzList 1 = [1]
collatzList n = n: (collatzList $ collatz n)
「n: $ collatzList $ collatz n」ってやったら怒られた。
= 三段論法(笑)
main = print $ die "socrates"
die x | human x = True
| otherwise = "unknown"
human "socrates" = True
= おお、演算子をかっこで囲うと前置にできるんだそうな。 100までの素数を求めるコードはこうなる。
main = print $ filter isPrime [2..100] isPrime :: Int -> Bool isPrime x = all ((<) 0) $ map (mod x) [2..x-1]たぶん関数の合成を使うともっとスマートに書けるんだろう。
=
main = print $ filter (\x -> all (((<) 0).(mod x)) [2..x-1]) [2..100]あんまりスマートではない。
= 二項演算子に部分適用を使う場合、(0 <)とか(< 0)とか書けるそうな。
main = print $ filter (\x -> all ((0 <).(mod x)) [2..x-1]) [2..100]!=は/=らしい。
= Haskellでは関数適用が一番優先順位が高い。ひどい。 一般的な言語ではf(arg1, arg2, arg3)と関数を呼ぶ際に、 fとarg1の間のかっこや、arg1とarg2の間のカンマは最も優先度の低い演算子の役割をする。 でもHaskellの場合はf arg1 arg2 arg3の間の空白文字が一番優先度の高い演算子。見えないくせに。
というわけで(mod x)の周りのかっこはとりのぞける。
main = print $ filter (\x -> all ((0 <).mod x) [2..x-1]) [2..100]
= そんなわけで「ふつうのHaskell」を220ページまで読んだわけだけど、 いよいよ型と型クラス。どきどき。
モナド。どきどき。
モナドって、例えばリストに対して追加を行う関数appendは、適用する順番が大事だから、
append (append (append [] 1) 2) 3というように書かないといけないところを、
(bind (bind (return 1) append 2) append 3)と書けるようにbindとreturnを定義して、 bindを二項演算子に変えて
(((return 1) >>= append 2) >>= append 3)余計なかっこを取り除いて
return 1 >>= append 2 >>= append 3とした、というだけ?
= 自分でモナドを定義してみようとしたのだけど
No instance for (Show (m t))
arising from use of `print' at test.hs:1:7-11
Possible fix: add an instance declaration for (Show (m t))
In the first argument of `($)', namely `print'
In the expression: print $ (return 1)
In the definition of `main': main = print $ (return 1)
printがないって怒られる。
もう寝よう。
=
instance Show MyMonad where show (MyMonad x) = show(x)こんどはkind mis-matchといわれるようになった。
フィードバック
出来るかなー漠然と思ってたことが、本当に出来るんですね。Jythonすごい。
もうわかってるかもしれないがコメント
>関数適用は f arg1 arg2 arg3で Java風にいうとf(arg1, arg2, arg3)になるわけだけど、これって部分適用があるから f(arg1)(arg2)(arg3)なんだよね?
カリー化関数だから,
(((f arg1) arg2) arg3)だよね
>一般的な言語ではf(arg1, arg2, arg3)と関数を呼ぶ際に、 fとarg1の間のかっこや、arg1とarg2の間のカンマは最も優先度の低い演算子の役割をする。
関数呼出しに()と,をくっつけるかそうでないかの違いだからしょうがない.つか,関数適用((f arg1) arg2) ...の()を省略してもいいよっていう規則なだけなんだけど.わかりにくければ()を常につければいい.
あー,たしかに関数適用が他の演算子より優先度が高いと,そういうところで気持ち悪いね・・・.納得です.