初めての Julia Lang 1.0
インストール後、一度も触っていなかった Julia ですが、休みの徒然に Fortran のプログラムを移植してみることにしてみました。
fortran66.hatenablog.com
スクリプト系なので、題材は画像など I/O が面倒くさいものが良かろうと思い、いつもの ASCII ART プログラムに挑戦することにしてみました。しかし、データ構造などがよく分からないままいじれてしまうので、完全な移植はやめて、短く済ませることにしました。
Julia Box での実行
julia box で実際に実行できます。
https://www.juliabox.com/
ログイン後、Dashboard から GitHub からのクローンを選択して、以下のアドレスをコピペしてやればよいです。Jupyter Notebook が立ち上がります。(lab にはならないようです。)
https://github.com/f66blog/Julia10AA.git
実行には、画像読み込みのパッケージが必要ですが、これの読み込みに時間がかかります。
using Pkg Pkg.add("Images") Pkg.add("FileIO") Pkg.add("ImageMagick")
using Images using FileIO function rgb_norm(x) fr = 0.3 fg = 0.6 fb = 0.1 fr * red(x) + fg * green(x) + fb * blue(x) end function distance(x, y) sum(map(rgb_norm, x - y)) end function to_ascii(font, im) d = fill(0.0, 95) for i = 1:95 d[i] = distance(font[i], im) end Char(Int(' ') + findmin(d)[2]) end function AA(font, img) ny, nx = size_spatial(img) for iy = 1:8:ny - 7 for ix = 1:4:nx - 3 char = to_ascii(font, img[iy:iy+7, ix:ix+3]) # over/under exposure correction arbtary_parameter = 4.75 r = distance(font[1], img[iy:iy+7, ix:ix+3]) / arbtary_parameter + 1 n = convert(Int, floor(r)) if char == 'R' # black char = ['*', '*', '*', '=', '=', 'H', '#', 'M', 'R'][n] elseif char == '!' # white char = ['.', ',', '`', '`', '"', '-', '~', ':', '!'][n] end print(char) end println() end end function main(font_file, file_name) # load and chop fonts fonts = load(font_file) fo = fonts[:, 1:4] # Array{RGB{Normed{UInt8, 8}}, 2} font = fill(fo, 95) for i = 1:95 font[i] = fonts[:, 4*i-3:4*i] # display(font[i]) # font = 0.6 .* font end # load image img = load(file_name) # main AA(fonts, img) end main("font4x8.gif", "tktt.jpg")
- 作者: Michael Metcalf,John Reid,Malcolm Cohen
- 出版社/メーカー: Oxford Univ Pr
- 発売日: 2018/11/06
- メディア: ハードカバー
- この商品を含むブログを見る
寝言
スクリプト系だけあって、データ形式も分からないまま画像を読み込んで、Jupyter 上の IJulia 環境で少しづつ試しながら進んで行けるのがとても便利でした。Fortran と比べると、様々な定義部がないのでプログラムも、とても短く書けました。
同時に、データ構造の定義がよく分からないので、一度引っかかると万事窮すという感じでした。構造体(タプル)の配列の配列みたいなものが出てきて、中々手ごわかったです。?変数、でデータ構造の情報が取れますが、その情報を基に特定サイズの配列を宣言するのに大変手間取りました。結局、宣言するのをあきらめて、実際の単位構造を fill する形で(コピペ増殖)で実現しました。
イライラする点は、たかが画像ファイルを読み込むためだけに、数ギガバイトのパッケージをカスケード的に勝手に読み込みはじめる点で、利便性とのトレードオフですが、現代の計算機環境からすれば数ギガバイト程度は、チリのようなものなのでしょう。
さて Fortran の場合、プログラムは全体像がイメージされないと書き始める気のしないトップダウン的な感じですが、Julia ではちょこちょこと小さい部品を試している内に、さざれ石の巌となる感じで全体が出来上がるボトムアップ的な感じでやれました。君が代的な言語と言えましょう。
Charles Anthony Richard Hoare:
You can't teach beginning programmers top-down design because they don't know which way is up.
命令に関しては、とりあえず後発言語だから Fortran にある便利命令は、一通りそろっている感じでしたが、elemental な演算を、いちいち . をつけたり map 命令で書かなければならないのに引っかかりました。データ構造が分からないので for 文を書かざる負えない所もありました。
これは型推論の自由度とのトレードオフの関係かとも思います。(Fortran の場合、データ構造が配列一本鎗なので、この辺が単純になります。スカラー変数に対して適切に関数・サブルーチンを定義しておけば、配列を引数に与えただけで要素毎の全配列演算になります。演算子も、Julia の . をつけるような演算が . 無しで実行されます。この種の機能は組み込み型では Fortran 90 で、ユーザー定義副プログラムでは Fortran95 で、約30年前から実装されています。)
それにつけても Julia の文法がよく分かってない上に、ネット情報は ver 0.6 以前のものばかりで、全く頼りにならず、公式サイトの検索もイマイチで、実数を整数に変換するだけで、1時間以上検索しても答えが見つからずホトホト困りました。Fortran なら1秒で解決するのにと思うと、Python 以上にマジむかつきましたw
エラーメッセージをじっと眺めた結果、どうも切り捨てを拒否しているらしいと分かったので、floor を取った後に変換することで何とか解決しました。(つまり 2.0 は 2 にできるが、2.05 は 2 にできない。)
n = convert(Int, floor(r))
型推論とかの関係で、型の包含関係を導入するから、こういう発想になるのだろうと思います。整数が浮動小数点数と同じ数直線上に置かれていると思っているのでしょう。
個人的には、整数と浮動小数点数は完全なる別物と考えた方がいいと思います。整数は離散値で、浮動小数点数は、数直線を非等長な区間で区切った区間毎の代表元と思った方が、計算結果を理解するうえで適切な状況が多いと思います。
数直線の区分化という意味では、単精度と倍精度も異なる区間の代表をもつ別物と考えた方がいいと思います。整数⊂単精度⊂倍精度の包含関係は、どの位好ましいのか、判断がつきません。
数値型の包含関係は、その昔 N. Wirth が導入しているし、まぁいいですw
Binder での Julia
以下で v0.6 系が pip で導入されています。v1.0 系が出る前の記事です。
github.comblog.jupyter.org