スクリプト4(どこでもmy関数)
このページはマス旅の一部です。
PCがあればすぐできる「スクリプト」を使った数学を考えましょう。
今回は、自分の手で計算機をアップデートする、「どこでもmy関数」
1.自作関数の作り方と使い方
組み込み関数がないけれど、よく使う計算があるときどうしますか?
一般的な言語ならパッケージをインストールするのが普通ですが、
BS(bash)やPS(PowerShell)の良さは「手軽さ」です。
サクッと自分で作ってしまいましょう。
まずは「2乗を返す関数 sq」で基本を知ろう。
<関数の基本(BS)>
引数(渡された値)は $1, $2 で受け取り、echo で値を返します。
「return」を「echo」と書くと覚えればいいですね。
`関数の名前() { 定義の中身 }`
の形で定義しましょう
(`function` というキーワードは省略できます)。
呼び出すときは変数とちがって、$ を頭につける必要はありません。
いたってシンプル!
# 関数の定義
sq() { echo $(($1 * $1)); }
# 関数の呼び出し
sq 5
[OUT] 25
# 作ったそばからワンライナーで使うのもアリです
sq() { echo $(($1 * $1)); }; sq 5
<関数の基本(PS)>
引数を受け取り、値を評価して返します。
returnをつけなくても動きますが、動作安定のために明示的につけることをおすすめします。
`function 関数の名前($引数名) { 定義の中身 }`
と書きます。
引数にも $ をつけるのがポイントです。
# 関数の定義
function sq($n) { return $n * $n }
# 関数の呼び出し
sq 5
[OUT] 25
# 一行で同時に実行する場合
function sq($n) { return $n * $n }; sq 5
2.数学関数を作る
基本が分かったら、数学の面白い関数を自分で作ってみましょう!
<コラッツの1ステップ関数:collatz_next>
「偶数なら2で割る、奇数なら3倍して1を足す」処理を行います。
//BS
collatz_next() { if (($1 % 2 == 0)); then echo $(($1 / 2)); else echo $((3 * $1 + 1)); fi }
collatz_next 6 # [OUT] 3
collatz_next 7 # [OUT] 22
//PS
function collatz_next([int]$n) { if ($n % 2 -eq 0) { return [int]($n / 2) } else { return [int](3 * $n + 1) } }
collatz_next 6 # [OUT] 3
collatz_next 7 # [OUT] 22
処理の中身だけに集中して書けるので、分かりやすいですね。
<コラッツ系列を出力する:collatz_seq>
さきほどの collatz_next を繰り返し使って、1に到達するまでの系列を出してみましょう。
//BS
複数行のコマンドの入力方法:
1行入れてエンターを押すと、行継続プロンプト `>` が出ます。
最後の `}` を入れてエンターを押すと通常のプロンプトに戻り、無事に読み取られたことが分かります。
Pythonではないのでインデント(字下げ)は気にしなくて大丈夫ですが、
読みやすさを考えてスペースを空けましょう。
while文の中で「関数を呼び出した結果」を変数に代入するときは、
`n=$(collatz_next $n)` のように `$()` で囲んで呼び出します。
$n が上書きされていき、1になるまでズラリとコラッツ数列が出力されます。
collatz_seq() {
local n=$1
echo -n "$n "
while (( n > 1 )); do
n=$(collatz_next$n)
echo -n "$n "
done
echo ""
}
# 利用例
collatz_seq 7
[OUT] 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
//PSでも1行入れてエンターを押すと行継続記号 `>>` が表示されます。
function collatz_seq([int]$n) {
Write-Host "$n " -NoNewline
while ($n -gt 1) {
$n = collatz_next$n
Write-Host "$n " -NoNewline
}
Write-Host ""
}
# 利用例
collatz_seq 7
[OUT] 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
3.コード化
<python>
pythonの環境はjupyter notebookなどでやるのが普通ですが、
対話画面のスクリプト言語としてもできます。
BSやPSと同じように、一行入れたらエンターを押すと
行継続インデント...がでます。
貼り付けに失敗した場合は、4spaceが1tabの標準ですから、
数えて行頭を調節しましょう。
また、最終行は2回エンターすると、ふつうのプロンプト>>>に
もどります。
import math
# 1. コラッツ列の長さ(ステッブ数)を返す関数
def collatz_steps(n):
steps = 0
while n > 1:
n = n // 2 if n % 2 == 0 else 3 * n + 1
steps += 1
return steps
# 2. オイラーのφ関数
# nとkが互いに素というのはGCD(n,k)==1のことでした。
# 定義をそのままかいただけで自前関数ができるというのが素敵ですね。
def phi(n):
return sum(1 for k in range(1, n + 1) if math.gcd(n, k) == 1)
print(f"Collatz steps for 7: {collatz_steps(7)}") # OUT: 16
print(f"phi(12): {phi(12)}") # OUT: 4
<geogebra>
# オイラーのφ関数はGeoGebraには組み込み関数はありません。
# 自分でリスト操作を使って自作する場合、
# KeepIfでpythonと同様に1行でかけそうです。
phi_my = Length(KeepIf(GCD(n, k) == 1, k, Sequence(i, i, 1, n)))
# しかし、論理的に正しくても、geogebraはコンパイルして変数依存形にするルールがあるため
# 未定義変数を使った関数は、x,y,zのような座標系以外、基本的に困難です。
# nを数論関数の変数として認識してくれません。
# そういうときは、リスト変形を多段階で地道にやりましょう。
n=Slider(1,100,1)
# このあとに、一般関数としてではなく、数値nを使ったコマンド連鎖として関数をパイプラインした値の変数名がphiです。
phi= Length(KeepIf(GCD(n, k) == 1, k, Sequence(i, i, 1, n)))
# コラッツの1ステップ(Ifコマンドで分岐)
c(x) = If(Mod(x, 2) == 0, x / 2, 3x + 1)
これも、xが予約語であり、xは連続量の変数だから、Mod(x,2)とぶつかります。
そこで、変数のnを数値として渡されるから、
collazN = If(Mod(n, 2) == 0, n / 2, 3n + 1)
text0="φ(" + n + ")=" + phi
text1="collaz_{next}(" + n + ")=" + collazN
<振り返り>
GeoGebraやPythonが驚くほど簡単に書ける理由は、
組み込み関数が豊富なこと、変数や区切り記号のルールが単純なこと、
そして「リスト」という強力なデータ構造が使えるからです。
しかし、小さなパーツ関数を自作して組み合わせれば、
BSやPSといった身近なスクリプトでも自分だけの計算機を作ることができます。
あくまでも「頭の体操」として、時間の余裕があるときに
チャレンジしてみてください。
また、どんな言語も独自のルールがあります。扱いやすいはずのgeogebraにも
メモリ空間、名前つけのルールが
JSをベースにして作られていますので、不自由な部分もあります。
そこのところは、経験から学びましょう。
もちろん、実際の仕事で高度な関数やモジュールが必要になったときは、
自分で一から作ろうとせず、すでに用意されているライブラリやAIを上手に頼る
のが一番スマートです!