抵抗ってなんだっけ Lispで分数計算(その1)

せっかくラズパイを購入したので、電子工作もしていきますが、まずは電子回路の基礎オームの法則の勉強から始めます。

resister

電圧(E) = 抵抗(R) x 電流(I)

電圧値5Vで、抵抗値が100kΩなら、電流値は、

5 ÷ 100000 = 0.00005 (5マイクロA)

resister-tandem
上図のように、抵抗を直列に繋げるとき、抵抗値は、R1+R2となります。

resister-para
一方、並列に繋げると、抵抗値は、1/(1/R1 + 1/R2) となります。複雑ですね。今回のコマンドラインの主題は、この分数計算となります。

コマンドラインで分数は使えるのでしょうか。例えば、pythonで1(被除数)÷3(除数)を求めようとした場合、

~$ python
>>> 1 / 3
0

なんてことでしょう。答えが0になりました。たいていのプログラミング言語は、このように整数どうしの割り算は、結果が切り捨てされます。
小数点以下を求めたい場合は、被除数また除数を浮動小数点数で表現します。

~$ python
>>> 1.0 / 3
0.3333333333333333
>>> 
~$ irb
irb(main):001:0> 1/3
=> 0
irb(main):002:0> 1.0/3
=> 0.3333333333333333
irb(main):003:0> 

浮動小数点数を使った計算は、精度の問題があります。下記では1を49で割って49で掛けただけなので、数は1のまま変わらないはずですが、結果は1になっていません。

~$ irb
>>> 1.0/49*49
0.9999999999999999
>>> 

まあ、実際抵抗値の計算では、そこまでの精度は不要かと思いますが、分数計算したい場合、LISP系言語が便利です。
LISP(scheme系)言語Gauche(ゴーシュ)を使ってみましょう。インストールはDebianではsudo apt-get install gauche。Gaucheシェルの実行はgoshを実行します。

~$ gosh
gosh> (+ 1 2)
3

1 + 2を計算してみました。
ん? よくわからない。それはそのはずです。LISPの式は学校で習った足し算の書き方と大きく異なります。
まず括弧。LISP言語のプログラムはかならず括弧がつきます。括弧は、リストを表します。

(要素1 要素2 要素3 要素4 … )

リストは上記のような構成ですが、要素1に、命令を書きます。 上記コマンドラインの例で足し算”+”の部分です。
余談ですが、私はキー入力する際、シフトキーが必要になるため()がとても疎ましく思えますが、LISPの()で囲む言語仕様自体はものすごく美しいと感じています。
引き算をしてみましょう。

gosh> (- 5 2)
3

リストなので、数をたくさん扱えます。

gosh> (+ 1 2 3 4 5 6 7 8 9 10)
55
gosh> (- 1 2 3 4 5 6 7 8 9 10)
-53

さて、割り算をしてみましょう。

gosh> (/ 1 3)
1/3

結果は、1/3 つまり3分の1、分数がそのまま出ました。分数となるため、浮動小数点数計算のように精度落ちがありません。
1 ÷ 49 × 49 を計算してみましょう。

gosh> (* (/ 1 49) 49)
1
gosh> 

一致しました。
もちろんgoshでも浮動小数点数が使えるので、以下のように不一致となります。

gosh> (* (/ 1.0 49) 49)
0.9999999999999999
gosh> 

では抵抗の計算に戻ります。並列接続の時、計算式は、
1/(1/R1 + 1/R2)
でした。
R1=20、R2=30として、goshで計算すると、

gosh> (/ 1 (+ (/ 1 20) (/ 1 30)))
12
gosh> 

では、これをbash関数にして、R1、R2を引数指定できるようにしてみます。

~$ resister(){
	gosh -E"print (/ 1 (+ (/ 1 $1) (/ 1 $2)))" -Eexit
}

gosh の-Eオプションは一番外側の()を内部でつけてくれます。
-Eexitは、exitをしないとgoshシェルから抜けないので必要になります。

確認してましょう。

~$ resister 20 30
12
~$ resister 20 20
12
~$ resister 15 16
240/31

結果が分数で出てしまいました。途中計算は分数で良いのですが、最終結果は小数点で表現したいので、少し改造します。

~$ resister(){
	gosh -E"print (/ 1.0 (+ (/ 1 $1) (/ 1 $2)))" -Eexit
}

1を1.0に変えただけです。再度確認してみます。

~$ resister 15 16
7.741935483870967
~$ resister 100 30
23.076923076923077
~$ resister 20 30
12.0
~$ resister 20 20
10.0
~$

コメント

タイトルとURLをコピーしました