はじめての Python プログラミング

この Notebook は,

のプログラミング部分に相当する内容を,JavaScript のかわりに Julia を使って書いてみたものです。比較・対比しやすいように章番号を合わせています。

テキストとは関係なく,初めて Jupyter Notebook で Julia を利用する方にとっても参考になるかも知れません。 この Notebook では,プログラミング言語としての共通部分に焦点を絞って解説します。

では,はじめましょう。

なお,本稿執筆時点では,Julia の Jupyter kernel である IJulia を sysytem-wide にインストールすることはできないようです。利用者各自が最初に1回だけ,以下の手順を行う必要があります。

  1. ターミナルで julia を入力して対話型環境(REPL)に入る。
  2. julia> のプロンプトに ] を入力してパッケージマネージャモードに入る。
  3. add IJulia と入力してしばらく成り行きを見守る。
  4. 以上。(Ctrl-D で終了)
$ julia
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.5.3 (2020-11-09)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> ]

(@v1.5) pkg> add IJulia

はじめてのプログラミング

エンゲル係数とは,家計の消費支出に占める飲食費(食料費,食費)の割合(パーセント単位)です。

消費支出 100,000円,飲食費 40,000円の人のエンゲル係数を求める例。Julia では以下のような電卓的な使い方ですぐ答えが出ます。

本稿のように Jupyter Notebook 環境では,以下の1行を書いたあとに,Shift キーを押しながら Enter キー(または Return キー)を押して実行します。

In [1]:
40000/100000 * 100
Out[1]:
40.0

ここからは,単に電卓的に使うだけではなく,応用・発展も見据えて以下のようなプログラム文を作成して実行することにします。

以下の例では,

  1. 1行目で変数 s1 に消費支出である 100000(円)という数値を代入し,
  2. 2行目で変数 s2 に飲食費である 40000(円)という数値を代入し,
  3. 3行目でエンゲル係数を計算して,変数 x1 に代入し,
  4. 4行目でエンゲル係数の値 x1 を表示させています。println() は表示後,改行します。
In [2]:
s1 = 100000
s2 = 40000
x1 = s2*100/s1
println(x1)
40.0

エラーとデバグ

意に反して,プログラムがうまく動作しない場合があります。たとえば,上記の 3行目を以下のように書いた場合。

掛け算を示す演算子は * であるところを誤って × と書くと,これらの記号は Julia では演算子として定義されていないのでエラーとなります。Jupyter Notebook では,以下のように誤りやその箇所を指摘してくれます。

In [3]:
s1 = 100000
s2 = 40000
x1 = s2 × 100 ÷ s1
println(x1)
UndefVarError: × not defined

Stacktrace:
 [1] top-level scope at In[3]:3
 [2] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091

別の種類の誤りもあります。たとえば,「100」を「1000」と入力した場合...

In [4]:
s1 = 100000
s2 = 40000
x1 = s2 * 1000 / s1
println(x1)
400.0

上記の例ではエラーの警告が出ませんが,答えは明らかに変です。(エンゲル係数の最大値は100だから。)

このような場合はコンピュータにとって誤りではなく,指示通りに計算し,その結果を表示します。 このようなプログラム作成者の不注意や勘違いによって生じるエラーには注意が必要です。

プログラムが正しく動作しない場合は,誤りの箇所を探し,修正して再度動作を確認します。正しく動作するようになるまで,プログラムを修正し,実行(Shift + Enter)します。この作業をデバグといいます。

データの型と変数

上記の例の 1行目では,s1 という名前の変数に 100000 という 数値を代入しています。変数名はアルファベットで始まる英数字列にします。大文字と小文字は区別されます。

変数 ss に文字列を代入する場合は,以下のように " で囲みます。

In [5]:
ss="エンゲル係数"
println(ss)
エンゲル係数

式と演算

掛け算を示す演算子は *,割り算を示す演算子は /。足し算は +,引き算は - です。

Julia では,文字列の連結演算は * です。他の言語では + を使うこともあるかと思いますが,Juliaでは連結演算は * です。 以下の例を参照。

In [6]:
x0 = "エンゲル"
y0 = "係数"
z0 = x0 * y0
println(z0)
エンゲル係数

文字列と数値を直接連結することはできません。

In [7]:
s1 = 100000
s2 = 40000
x1 = s2*100/s1
z1 = "エンゲル係数は " * x1
println(z1)
MethodError: no method matching *(::String, ::Float64)
Closest candidates are:
  *(::Any, ::Any, !Matched::Any, !Matched::Any...) at operators.jl:538
  *(!Matched::Complex{Bool}, ::Real) at complex.jl:309
  *(!Matched::Missing, ::Number) at missing.jl:115
  ...

Stacktrace:
 [1] top-level scope at In[7]:4
 [2] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091

そんなときは,string() 関数で数値を文字列に変換して連結します。

In [8]:
s1 = 100000
s2 = 40000
x1 = s2*100/s1
z1 = "エンゲル係数は " * string(x1)
println(z1)
エンゲル係数は 40.0

println() のところは,文字列にして連結しなくても,以下のようにも書くことができます。 println() 文は,カンマ (,) で区切ることによって複数の項目(変数)を表示することができます。

In [9]:
println("エンゲル係数は ", x1)
エンゲル係数は 40.0

もう一つの方法として,Julia では「文字列の補完」があります。$ の後に変数名を記述すると,文字列になります。以下の例を参照。

In [10]:
println("エンゲル係数は $x1")
エンゲル係数は 40.0

主な演算子

加減乗除の演算子 +, -, *, /

優先的に計算したい箇所は丸括弧 ( )で グループ化します。

In [11]:
2 + (50 - 5 * 6)/4
Out[11]:
7.0

除算 / は常に浮動小数点数を返します。 ÷ 演算子は 整数除算を行い、(小数部を切り捨てた) 整数値を返します。剰余は、% で求めます。 冪乗は ^ です。

In [12]:
17 / 3
Out[12]:
5.666666666666667
In [13]:
17 ÷ 3
Out[13]:
5
In [14]:
17 % 3
Out[14]:
2
In [15]:
2^4
Out[15]:
16

対話型プログラム

上記の例では,プログラムの中で消費支出や飲食費を決めていましたが,今度はプログラムを実行する人が入力できるようにしてみます。

Base.prompt() 関数は入力された「文字列」を返すので,数値として計算するために parse(Float64, ) で数値(浮動小数点数)に変換します。

In [16]:
s1 = Base.prompt("消費支出? ")
s2 = Base.prompt("飲食費? ")
s1 = parse(Float64, s1)
s2 = parse(Float64, s2)
x1 = s2 * 100 / s1
println("エンゲル係数は", x1)
消費支出? : 100000
飲食費? : 40000
エンゲル係数は40.0

関数の定義と呼び出し

(これまでの例では1回しか計算していませんが)よく使う計算を「関数」として定義する例です。

以下の例では,1行目から4行目でエンゲル係数を計算する関数 calc() を定義し,8行目でその関数を呼び出しています。

# から始まる行はコメント(実行に影響しない注釈)です。

In [17]:
function calc(s1, s2)
    # 消費支出 s1 円と飲食費 s2 円からエンゲル係数を計算する。
    return s2*100/s1
end

s1 = Base.prompt("消費支出? ")
s2 = Base.prompt("飲食費? ")
s1 = parse(Float64, s1)
s2 = parse(Float64, s2)
x1 = calc(s1, s2)
println("エンゲル係数は ", x1)
消費支出? : 100000
飲食費? : 40000
エンゲル係数は 40.0

いったん定義された関数は,以下のように何回でも利用できます。

In [18]:
calc(120000, 53000)
Out[18]:
44.166666666666664

(練習)

消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。

数学関数を使う

Julia では,$\sin x, \cos x, \tan x$などの数学関数が組み込まれています。

In [19]:
# pi は円周率
pi, sin(pi/2), cos(pi/3), tan(pi/4)
Out[19]:
(π, 1.0, 0.5000000000000001, 0.9999999999999999)

また,切り捨て floor(),切り上げ ceil(),数値の丸めを行う round() なども利用できます。

以下では,floor() 関数を使って小数点以下を切り捨てたエンゲル係数の値を表示させています。小数点以下を切り捨てた以上,答えは整数となるので,第1引数に Int を指定して整数に変換します。

また,エンゲル係数を計算する部分はすでに関数 calc() として定義していますが,プログラム全体を関数 engel() として定義してしまいます。

In [20]:
function engel()
    s1 = Base.prompt("消費支出? ")
    s2 = Base.prompt("飲食費? ")
    s1 = parse(Float64, s1)
    s2 = parse(Float64, s2)
    x1 = calc(s1, s2)
    println("エンゲル係数は ", floor(Int, x1), 
            " です。(小数点以下切り捨て)")
end
Out[20]:
engel (generic function with 1 method)
In [21]:
engel()
消費支出? : 120000
飲食費? : 43000
エンゲル係数は 35 です。(小数点以下切り捨て)

(練習)

小数点以下を切り上げたエンゲル係数の値を表示させるように上記のプログラムを変更しなさい。

参考:数値の丸めと四捨五入

Python の組み込み関数に round() があります。数値を丸める関数ですが,いわゆる四捨五入とはちょっと違うようです。

以下の例からわかるように,小数点以下が 0.5 未満の場合と 0.5 を超える場合は四捨五入と同様の丸め方ですが,ちょうど 0.5 のときは,偶数に丸められていることがわかります。

これを偶数への丸めといい,端数が 0.5 より小さいなら切り捨て,端数が0.5 より大きいなら切り上げ,端数がちょうど 0.5 なら切り捨てと切り上げのうち結果が偶数となる方へ丸めます。

すでに利用した floor() 関数と同様に,round() 関数も数値を丸めたて整数として表示させたいときには以下のように第1引数に Int を指定します。

In [22]:
round(Int, 12.49), round(Int, 12.50), round(Int, 12.51)
Out[22]:
(12, 12, 13)
In [23]:
round(Int, 13.49), round(Int, 13.50), round(Int, 13.51)
Out[23]:
(13, 14, 14)

端数がちょうど 0.5 のときに切り上げて 四捨五入 の動作をさせるには,round() 関数に以下のように RoundNearestTiesUp オプションをつけます。

In [24]:
round(Int, 12.50), round(Int, 12.50, RoundNearestTiesUp)
Out[24]:
(12, 13)

条件分岐

条件分岐とは,例えばエンゲル係数の値によって実行文を変えることです。例を示します。

関数 engel() を以下のように変更します。

In [25]:
function engel()
    s1 = Base.prompt("消費支出? ")
    s2 = Base.prompt("飲食費? ")
    s1 = parse(Float64, s1)
    s2 = parse(Float64, s2)
    x1 = calc(s1, s2)
    if x1 >= 80
        println("エンゲル係数は ", round(Int, x1), 
                " です。飲食費を使いすぎです。")
    else
        println("エンゲル係数は ", round(Int, x1), " です。")
    end    
end
Out[25]:
engel (generic function with 1 method)
In [26]:
engel()
消費支出? : 100000
飲食費? : 80000
エンゲル係数は 80 です。飲食費を使いすぎです。
In [27]:
engel()
消費支出? : 100000
飲食費? : 40000
エンゲル係数は 40 です。

条件分岐の if 文の書式は以下のとおりです。

if 条件式1
    条件式1 が満たされた場合に実行する文
elseif 条件式2
    条件式2 が満たされた場合に実行する文
else
    それ以外の場合に実行する文
end

(練習)

上記の条件分岐を,エンゲル係数が

  1. 80 以上の場合には「飲食費を使いすぎです。」
  2. 80 未満 40 以上の場合には「正常です。」
  3. 40 未満の場合には「もっと食べましょう。」

と表示させるように変更しなさい。

繰り返し処理

以下の例では,1行目で代入された消費支出に対してエンゲル係数を計算します。

1回だけ計算して表示するのではなく,2行目の飲食費の初期値(ここでは 40000 円)から,5行目の条件式(ここでは 80000 円以下)が成り立つ場合に,8行目にあるように 5000 円ずつ増やしながらエンゲル係数を表示します。

In [28]:
s1 = 100000
s2 = 40000
println("消費支出 ",s1,"円に対するエンゲル係数の値")

while s2 <= 80000
    x1 = calc(s1, s2)
    println("   飲食費 ", s2, "円の場合: ", x1)
    s2 = s2 + 5000
end
消費支出 100000円に対するエンゲル係数の値
   飲食費 40000円の場合: 40.0
   飲食費 45000円の場合: 45.0
   飲食費 50000円の場合: 50.0
   飲食費 55000円の場合: 55.0
   飲食費 60000円の場合: 60.0
   飲食費 65000円の場合: 65.0
   飲食費 70000円の場合: 70.0
   飲食費 75000円の場合: 75.0
   飲食費 80000円の場合: 80.0

while 文の書式は以下の通りです。

while 条件式
    条件式が成り立つ場合に実行する式
end

同様の繰り返し処理を for 文を使って行うこともできます。

In [29]:
s1 = 100000
println("消費支出 ",s1,"円に対するエンゲル係数の値")

for s2 = 40000:5000:80000
    x1 = calc(s1, s2)
    println("   飲食費 ", s2, "円の場合: ", x1)
    s2 = s2 + 5000
end
消費支出 100000円に対するエンゲル係数の値
   飲食費 40000円の場合: 40.0
   飲食費 45000円の場合: 45.0
   飲食費 50000円の場合: 50.0
   飲食費 55000円の場合: 55.0
   飲食費 60000円の場合: 60.0
   飲食費 65000円の場合: 65.0
   飲食費 70000円の場合: 70.0
   飲食費 75000円の場合: 75.0
   飲食費 80000円の場合: 80.0

for 文の書式は以下のとおりです。増分を省略すると 1 とみなします。

for i = 開始値:増分:終端値
    i が開始値以上,終端値以下の場合に実行される文
end

= の部分を in としてもよいようです。

In [30]:
for i in 1:5
    print(i, "  ")
end
1  2  3  4  5  

(練習)

以下の値を求めるプログラムを作成せよ。

$$\sum_{n = 1}^{10} n^2$$