#Julia言語

@ insert_before_after function f(x)
A
end begin
B
end begin
C
end



function f(x)
begin B end
begin A end
begin C end
end

になる。単にそれだけのマクロ。

gist.github.com/genkuroki/fe9c…
#Julia言語 スクショ
#Julia言語

struct A{T}
a::T
b::T
end

のとき

struct B{T}
@ fieldsof A{:T}
c::T
end



struct B{T}
a::T
b::T
c::T
end

と等価になるマクロ。

nbviewer.jupyter.org/gist/genkuroki…
#Julia言語

github.com/genkuroki/Inte…

野良パッケージ。添付画像のマクロを使えるようになる。

Meta.@ dump と @ show_tree の使い方は同じだが、後者の方が圧倒的に見易い。Juliaではコードの見た目とASTの対応が非自明なので、マクロを書くためにはこの手のツールが必須だと思う。
#Julia言語 野良パッケージ

@ printf マクロと @ sprintf マクロから、
printf 函数と sprintf 函数を作っている。

マクロじゃない printf 函数は結構欲強い場合がある。

github.com/genkuroki/Prin…
#Julia言語 のマクロ界の様子は LaTeX が存在しなかった時代の TeX 界にちょっと似ているかも。
#Julia言語

gist.github.com/genkuroki/fe9c…
insert_before_after macro

を更新。最初のAバージョンには函数の返り値が保たれないという欠陥がある。それを改善したBバージョンの例も作っておいた。

本当は function f(x) ~ end だけではなく、f(x) = ~ にも対応した方がよいのだが、やっていない。
#Julia言語 上でもちょっとふれたように、Juliaでは printf がマクロになっている。フォーマットを解釈しながら実行するのではなく、フォーマット専用のネイティブコードにコンパイルして実行する仕組みの方が速くなる。

函数版が欲しければ↓
github.com/genkuroki/Prin…
#Julia言語 「特殊函数の計算に必要な多項式や有理函数の分子分母の係数を自動的に計算してHorner法でべた書きするコードを吐くマクロ」を使って特殊函数を実装すると速くなる。しかも、この方法は一般性が高い(コンパイラやCPUなどに依存しない)。具体例が

nbviewer.jupyter.org/gist/genkuroki…

のIn[2]にある。
#Julia言語 図を作り直した。

函数の中身の前後に任意のコードを挿入するマクロの話です。

抽象構文木のレベルでどのように変更を施せばよいのかわかったら、それをそのまま素直にコードに翻訳すればよい。

nbviewer.jupyter.org/gist/genkuroki…

#Julia言語 マクロの中身は添付画像の通り。

quote ~ end で人間でも普通に読めるように書くのではなく、Expr(~) を使って書いているので、Juliaについて予備知識がない人は何をやっているか理解不能に見えるはず。

だから、1つ前のツイートの添付画像が必要だと思った。

やっていることは単純。
#Julia言語 S式風に書くと、

(:function funchead funcbody)



(:function funchead
(:block before (:= val funcbody) after val))

に書き変えて、funcbodyの前後にbefore, afterを挿入している。

: 付きは特別なシンボル。
#Julia言語 の式の表示の仕方は色々あります。

gist.github.com/genkuroki/9ffb…

exprに函数fの定義式を代入しています。

exprをそのまま表示させると、可読性の高いコードが表示される(添付画像1、Out[4])。

S式風にも表示できる(添付画像1、In[5])

添付画像2はツリー表示。

添付画像3はdump.
#Julia言語 の式の表示の仕方には、

* 通常のJuliaの文法に従った可読性の高い表示
* S式風
* ツリー
* dump

と色々ありますが、その実体はすべて同一です。その同一のものをいじって実行すれば、Juliaの式をいじって実行できたことになる。

それをやるのがマクロです。
#Julia言語 LispではS式による表示を主に取り上げるので、マクロについて考えるときに目が回らずに済み易いのですが、Juliaだと「通常のJuliaの文法に従った可読性の高い表示」と残りの3つのギャップが激し過ぎて目が回ります。

そこさえクリアできれば、Juliaのマクロが何をやっているか見えます。
#Julia言語 Juliaでの

Expr(:foo, a, b, c)

はS式の

(:foo a b c)

のようなもので、

Expr(:foo, a, Expr(:bar, b, c), d)

はS式の

(:foo a (:bar b c) d)

のようなものです。

Juliaの側にはExprが付いている分だけ複雑に見えるだけとも言える。
#Julia言語 Julia側ではExprがついている分だけ複雑に見えているだけで、見かけに騙されなければ、「S式を書くことと、Exprを使ってJuliaの式を書くことの難易度は原理的にはそう変わらないはずである」とも言える。

実際には人間の記憶容量の問題でそう簡単ではないのですが。
#Julia言語 大体において「原理的には」と言っている場合には「現実的にはちょっと無理」というニュアンスを含む。この場合もそうで、同じ型のRealなaとbの和を計算して表示するだけの函数をExprを使って書くと添付画像のようになる。Juliaの文法に従った可読性の高い表示のありがたみがよくわかる!
#Julia言語 のコード

function f(a::T, b::T) where T<:Real
print("a + b = ", a + b)
a + b
end

の様々な表示集。

表示の仕方は違っていても実体は同一であることの認識・理解が重要。

gist.github.com/genkuroki/9ffb…
#Julia言語 マクロが何をやっているかの説明用の画像を再度作り直した。非常にシンプルな内容なのですが、実際のコードの解読の仕方が分かり難い。

gist.github.com/genkuroki/fe9c…

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with 黒木玄 Gen Kuroki

黒木玄 Gen Kuroki Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @genkuroki

9 Oct
#Julia言語 【ネタ】tuple processing language

S式っぽいタプルでJuliaの式を与えると、それを解釈して実行してくれるマクロ(笑)

空行とコメントを合わせても33行しかありません。続く

gist.github.com/genkuroki/b410… Image
#Julia言語 タプル式でsin(π/6)を計算してみましょう。

(:call, :sin, (:call, :/, π, 6))

を実行すれば sin(π/6) を計算できます。:call を省略して

(:sin, (:/, π, 6))

でも同じ結果が得られるようにしてあります。続く Image
#Julia言語 タプル式で函数も定義できます。

(:(=), (:call, :f, :x), (:call, :sin, :x))

で f(x) = sin(x) と定義できます。In[4]ではそのようにして定義した f(x) を使って f(π/6) を計算しています。続く Image
Read 8 tweets
9 Oct
#Julia言語 「最初のプロットの遅延問題」は有名な欠点で、nightly buildで大きな改善されています。

遅いのは「最初のプロット」だけなので、プロット用のコードを書いたfoo.jlについて毎回

julia foo.jl

としていなければ大した問題にはならないです。

juliaは再起動の回数を減らして使いたい。
#Julia言語 REPL、Jupyter、Juno、VSCode内でJuliaを使い、コードを書き始める前に、

using Plots
plot(sin)

を実行しておいて、その後にプロット用のコードを書いて実行すれば実質的に待ち時間はゼロ。
#Julia言語 公式ドキュメントの

docs.julialang.org/en/v1/manual/w…

の方法:Jupyter

github.com/JuliaLang/IJul…

を使ったり、Revise.jl

github.com/timholy/Revise…

を使って、

julia> using Revise
julia> includet("foo.jl")
julia> plot_foo()
失敗→foo.jlを編集→編集結果が自動反映
julia> plot_foo()
Read 4 tweets
9 Oct
#Julia言語 nightly build で、パッケージA.jlの中のXをYという名前で使いたい場合に

using A: X as Y

とできるようになっていますね。

github.com/JuliaLang/juli…

添付画像はすでにIを使ってしまっているので、

using LinearAlgebra: I as E

としている場合。

gist.github.com/genkuroki/d812… ImageImage
#Julia言語 個人的には

import LinearAlgebra as linalg

として

A = randn(100, 100)
fac = linalg. lu(A)
e = linalg. eigen(A)

のように、「わざわざ」もしくは「必然性不明の短縮」で至る所 linalg. を付けて書くようなスタイルを使う人が

増えないで欲しいな

と思います。
#Julia言語 Pythonという特殊で一般性に欠けた世界の1つで普通になっている方法で import as を使う人が増えるのは、私も不快。

このスレのトップで紹介したように「すでにIを使っているので、LinearAlgebra.jlのIをEという名で使うため」に、

using LinearAlgebra: I as E

とできるのは便利。
Read 5 tweets
9 Oct
#統計

標本のばらつきの指標として
分散を採用する必然性は__ない__。
中央値との差の絶対値の加法平均も
立派なばらつきの指標である

という話は繰り返ししている。
しかも、Laplace分布モデルとの関係まで言及している。

繰り返し言及していることの証拠↓
twilog.org/genkuroki/sear…
#統計

標本平均と標本分散の計算
=正規分布モデルによる最尤推定

標本の中央値との中央値との差の絶対値の平均の計算
=Laplace分布モデルによる最尤推定

こういう関係。

「標本の代表値としてどれを重用するか」と
「どのような統計モデルで推定するか」の間には
上のような関係がある。
#統計 モデルが現実に合ってなければ捨てられるのはモデルの側なのに、実質正規分布モデルによる推定になっている「代表値としての分散の採用」にまるで必然性があるかのような説明をしようとしている場合が結構あるように見える。

大学の先生でもこの点はかなりひどいのでは?
Read 14 tweets
9 Oct
Re: RT #数楽

Bernoulli多項式を「特殊値」として持つゼータ函数はHurwitzのゼータ函数である。(その特別な場合がRiemannのゼータ函数)

問題:それと同様の意味でHermiteの多項式を「特殊値」として持つゼータ函数に類似はあるか?

答え:ある‼️↓
nbviewer.jupyter.org/github/genkuro… ImageImageImageImage
#数楽 区間[0,1]上の一様分布の分配函数(=モーメント母函数)の

Z(β) = ∫_0^1 e^{-βx} dx = (e^{-β} - 1)/(-β)

の逆数とベルヌイ数の母函数は本質的に一致し、統計力学の意味での[0,1]上のカノニカル分布

e^{-βx}/Z(β) = (-β)e^{-βx}/(e^{-β} - 1)

はベルヌイ多項式の母函数に本質的に一致する。
#数楽 Hurwitzのゼータ函数は[0,1]上の一様分布のカノニカル分布のMellin変換に本質的に一致する。

これを、ℝ上の(適当な条件を満たす)任意の確率分布のカノニカル分布のMellin変換に一般化すれば、一般化されたHurwitzのゼータ函数が定義される。
Read 6 tweets
8 Oct
#Julia言語 以前も書いたが、配列uを繰り返し更新するためにループの内側に

u = f(u, param)

と書くと、配列uの分のメモリ割当が毎回発生する。配列uの中身を書き換える函数 f!(u, param) を用意して

f!(u, param)

の形式で配列uを更新するのが、現時点での定跡(in-place計算)。
#Julia言語 公式ドキュメントを見ると、

y = f(x)

の形で使う返り値が配列の函数の多くについて、すでに用意された配列 y の成分を書き換えるスタイルの函数

f!(y, x)

が存在することが分かる。コードが少し分かりにくくなる犠牲を払って使う価値があるかどうかを考えることになる。
#Julia言語 函数fの中で使う作業用配列変数tmpが欲しければ

struct F{T}
tmp::Vector{T}
end
function (f::F)(x)
tmp = f.tmp
作業用配列tmpを使った計算
end

functin g(x, N)
tmp = Vector{eltype(x)}(undef, N)
f = F(tmp)
函数fを使う計算
end

のように書ける。続く
Read 10 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!

Follow Us on Twitter!