Diary

Diary

日々学んだことをアウトプットする場として初めてみました

分数の足し算引き算を AWK で

前回の記事で、AWK を使ってどのように分数の約分ができるのか、ということをやりました。

今回は、引き続き足し算と引き算をさせてみようと思います。

前回のまとめ

以下のようにして約分を行いました。詳しい方針は過去記事をご覧ください

$ echo 6/8 | tr '/' '\n' | factor |\
 awk 'NR == 1{for(i=2; i<=NF; i++){a[$i] += 1}} NR == 2{for(i=2; i<=NF; i++){a[$i] -= 1}};
   END{nume = 1; deno = 1; for(b in a){if (a[b] > 0){nume *= b**a[b]}
    else if (a[b] < 0){deno *= b**(-1*a[b])}} {print nume"/"deno}}'
3/4

AWK で分数計算を行なってみる

AWK で分数の足し算引き算を実装します

目標

Input: 1/2 ± 1/3 (など)

Output: 答え

一例

適時計算結果を表示させることで解説としたいと思います。

# awk で区切り文字" " として扱うために前処理
$ echo '1/3 + 1/6' | tr '/' ' ' 
1 3 + 1 6
# ここでは半角スペースで区切られていることが大事なので、
## 1/3+1/6 のような入力にも対応したければ、以下を挟む
$ echo '1/3+1/6' | sed -E 's@([0-9])([^ /0-9])([0-9 ])@\1 \2 \3@'
1/3 + 1/6

# 計算を行う。真ん中の符号が + か - かで場合分け
$  echo '1/3 + 1/6' | tr '/' ' ' |\
  awk '$3 == "+"{print ($1*$5 + $2*$4)"/"($2 * $5)}; 
    $3 == "-"{print ($1*$5 - $2*$4)"/"($2 * $5)}'
9/18

# 前回の訳文の結果を使えるように渡してあげる
$ echo '1/3 + 1/6' | tr '/' ' ' |\
 awk '$3 == "+"{print ($1*$5 + $2*$4)"/"($2 * $5)}$3 == "-"{print ($1*$5 - $2*$4)"/"($2 * $5)}' |\
  tr '/' '\n' | factor |\
   awk 'NR == 1{for(i=2; i<=NF; i++){a[$i] += 1}} NR == 2{for(i=2; i<=NF; i++){a[$i] -= 1}};
    END{nume = 1; deno = 1; for(b in a){if (a[b] > 0){nume *= b**a[b]} 
     else if (a[b] < 0){deno *= b**(-1*a[b])}} {print nume"/"deno}}'

# マイナスも試してみます
$ echo '1/3 - 1/6' | tr '/' ' ' | awk '$3 == "+"{print ($1*$5 + $2*$4)"/"($2 * $5)}$3 == "-"{print ($1*$5 - $2*$4)"/"($2 * $5)}' |  tr '/' '\n' | factor |   awk 'NR == 1{for(i=2; i<=NF; i++){a[$i] += 1}} NR == 2{for(i=2; i<=NF; i++){a[$i] -= 1}};
    END{nume = 1; deno = 1; for(b in a){if (a[b] > 0){nume *= b**a[b]} 
     else if (a[b] < 0){deno *= b**(-1*a[b])}} {print nume"/"deno}}'
1/6

おまけ

これまた Ruby なら一瞬です

$ echo '1/3 + 1/6' |\
  sed -E 's@^([0-9]+/[0-9]+)[ ]?\+[ ]?([0-9]+/[0-9]+)@puts \1r+\2r@g' |\
   ruby
1/2

おわりに

だいぶ AWK を使ったけいさんにも慣れてきたかと思います。

AWK に頼らないで生活できるよう目指していきたいです。