お客さん!おつり、おつり〜

世の中、つい忘れてしまうことがある。モノを買っておつりを受け取るのを忘れてしまい、スタッフさんから「おつり」と言われてしまう。しょっちゅうあったら困りものだがね。今はレジも変わり、レジのスタッフがお金を触ることがなくなってしまった。ある意味、トラブルが起きないようにする対策なのだろう。スタッフは商品にバーコードを通し、お客様は支払いとお釣りの受け取りをレジスターで行うようになった。ちなみに、近くにスーパーとセブンイレブン、ファミマがあるが、レジスターを導入しているのは、スーパーとセブンイレブンである。ファミマは従来のやり方だ。

お釣りを受け取り忘れるのは、レジスターが導入されたとしても起こる。わかっているとは思うが、他のお客様がお釣りを取り忘れたのを盗ったり、スタッフがお釣りを間違えたのを知って多く受け取ると、犯罪になるので注意しよう。もちろん、知らなかったでは済まされないのである。良い子はマネしてはいけない。

 

本題に入る。どうも、年を取ると話が長くなってしまうので申し訳ない。さて、今回は金種計算のコードについてお話しよう。金種計算とはアレである。そう、アレをこうするやつだ。うーん、なんだっけか。まあよい。

計算は基本的にお釣りから各金種で割った値の整数部を求めていけばよい。コード化するとこんな感じになる。使用するプログラミング言語は、わしの茶飲み友達であるFortran爺だ。本当はCOBOL嬢にしたかったのだが、あの子は忙しくらしくてな。

これでもかというくらいコメント書いておいたので“何しとるのか”わかるだろう。どんな小さなプログラムでも、手抜きをせずに丁寧にコードを書くのがプログラマーである。

! kind.f90
! 金種計算プログラム
program kinds

! 変数宣言
integer :: payment, purchase, change

! 購入金額と支払金額
purchase = 560
payment = 10000

! お釣りの計算
change = payment - purchase

! 計算結果の表示
print *
print *, '-- 金種計算プログラム --'
print *
write(*, '("お買い上げ金額 = ", i5, " 円")') purchase
write(*, '("支 払 い 金 額 = ", i5, " 円")') payment
print *, 'おつり = ', change, "円"
print *
print *, '金種                枚数'
print *, '------------------------'

! 各金種の枚数計算と表示
print *, '1000円 = ', int(change / 1000), '枚'
change = change - (int(change / 1000) * 1000)
print *, ' 500円 = ', int(change / 500), '枚'
change = change - (int(change / 500) * 500)
print *, ' 100円 = ', int(change / 100), '枚'
change = change - (int(change / 100) * 100)
print *, '  50円 = ', int(change / 50), '枚'
change = change - (int(change / 50) * 50)
print *, '  10円 = ', int(change / 10), '枚'
change = change - (int(change / 10) * 10)
print *, '   5円 = ', int(change / 5), '枚'
change = change - (int(change / 5) * 5)
print *, '   1円 = ', int(change / 1), '枚'

print *, '------------------------'
print *

stop
end program kinds

さて、基本形はできた。だが、コレで終わりではない。これで終わったらプログラマーがすたるのである。リストを見ればわかるはずだ。各金種の枚数計算部分が冗長すぎる、そして、いちいち変数に入力するのはメンドイ(ユーザービリティに欠けるともいう)。なので、ここから改良していく。

改良したプログラムが次のとおりである。冗長だった部分は、do文(for文)を使って短くし、支払金額の入力部分をwhile文とif文でチェックを行うようにした。ちなみに、exit文はPythonでいうbreak文、cycle文はcontinue文を指す。

! kind2.f90
! 金種計算プログラム(改良版)
program kinds2
    implicit none

    ! 変数宣言
    character(8), dimension(1:8) :: msg = (/"5000円", "1000円", " 500円", " 100円", "  50円", "  10円", "   5円", "   1円"/)
    integer, dimension(8) :: kinds = (/5000, 1000, 500, 100, 50, 10, 5, 1/)
    integer :: payment, purchase, change, number_sheet, i

    ! 購入金額と支払金額の入力
    purchase = 560
    do while(.true.)
        write(*, *)
        write(*,'("購入金額は", i4, " 円です")') purchase
        write(*, fmt='(a)', advance='no') 'お支払金額(0で終了) ? '
        read(*,*) payment

        if (payment > 10000) then
            print *
            print *, 'お客様、当店では10,000円以上は受け付けていません'
            cycle
        end if

        ! お釣りの計算
        change = payment - purchase

        if (payment == 0) then
            print *
            print *, '購入を中止します'
            print *
            exit
        else if (change < 0) then
            print *
            print *, 'お支払金額が足りません。もしかして、支払う気はない?'
            cycle
        else if (change > 0) then
            exit
        end if
    end do

    if (change > 0) then
        ! 計算結果の表示
        print *
        print *, '-- 金種計算プログラム --'
        print *
        write(*, '("お買い上げ金額 = ", i5, " 円")') purchase
        write(*, '("支 払 い 金 額 = ", i5, " 円")') payment
        print *, 'おつり = ', change, "円"
        print *
        print *, '金種                枚数'
        print *, '------------------------'

        ! 各金種の枚数計算と表示
        do i = 1, 8
            number_sheet = int(change / kinds(i))
            change = change - (number_sheet * kinds(i))
            write(*, '(" ", a8, " =       ", i5, " 枚")') msg(i), number_sheet
        end do

        print *, '------------------------'
        print *
    end if
    stop
end program kinds2

これで第一段階は終わりだ。しかし、改良はまだまだ続くのである。このプログラムは支払金額は10,000円までである。また、支払金額を入力する際に数値チェックを行っていない。文字列入れるとどうなるか。プログラムは停止する。当たり前のことだが、実際に使用するには、こういったチェックも実装する必要がある。

第二段階では、支払金額の入力は、文字列として入力して数値かどうかチェックすることを加えた。いろいろやっているが、Fortranの場合、文字列は型宣言時に領域を確保するようになっており、10文字の領域に3文字を代入しても長さは10文字なのである。比較するときは文字が入っていない場合、スキップしないといけないのである。まだまだ、改良の余地がある。

! kind3.f90
! 金種計算プログラム(さらに改良版)
program kinds3
    implicit none

    ! 変数宣言
    character(8), dimension(1:8) :: msg = (/"5000円", "1000円", " 500円", " 100円", "  50円", "  10円", "   5円", "   1円"/)
    character(10) :: str_payment
    integer, dimension(8) :: kinds = (/5000, 1000, 500, 100, 50, 10, 5, 1/)
    integer :: payment, purchase, change, number_sheet, i

    ! 購入金額と支払金額の入力
    purchase = 560
    do while(.true.)
        write(*, *)
        write(*,'("購入金額は", i4, " 円です")') purchase
        write(*, fmt='(a)', advance='no') 'お支払金額 ? '
        read(*,'(a10)') str_payment

        ! 数値文字列かどうかチェックする
        if (check_number(str_payment)) then
            print *, '金額が正しくないな、文字とか入力してない?'
            cycle
        end if

        ! 数値に変換する
        read(str_payment, '(i10)') payment

        ! 10000円以上は受け付けない
        if (payment > 10000) then
            print *
            print *, 'お客様、当店では10,000円以上は受け付けていません'
            cycle
        end if

        ! お釣りの計算
        change = payment - purchase

        if (payment == 0) then
            print *
            print *, '購入を中止します'
            print *
            exit
        else if (change < 0) then
            print *, 'お支払金額が足りません。もしかして、支払う気はない?'
            cycle
        else if (change > 0) then
            exit
        end if
    end do

    if (change > 0) then
        ! 計算結果の表示
        print *
        print *, '-- 金種計算プログラム --'
        print *
        write(*, '("お買い上げ金額 = ", i5, " 円")') purchase
        write(*, '("支 払 い 金 額 = ", i5, " 円")') payment
        print *, 'おつり = ', change, "円"
        print *
        print *, '金種                枚数'
        print *, '------------------------'

        ! 各金種の枚数計算と表示
        do i = 1, 8
            number_sheet = int(change / kinds(i))
            change = change - (number_sheet * kinds(i))
            write(*, '(" ", a8, " =       ", i5, " 枚")') msg(i), number_sheet
        end do

        print *, '------------------------'
        print *
    end if
    stop
    contains
    ! 数値チェック
    logical function check_number(strpayment)
        implicit none

        character(10) :: strnumber
        character(10) :: strpayment
        character(1) :: s
        logical :: flg

        ! 一文字ずつ取り出して数値文字列に含まれるか
        ! チェックする
        flg = .false.
        strnumber = '0123456789'
        strpayment = trim(adjustl(strpayment))
        do i = 1, len(strpayment)
            s = strpayment(i:i)
            if (s == '') then
                exit
            else if (index(strnumber, s) == 0) then
                flg = .true.
                exit
            end if
        end do
        check_number = flg
    end function check_number
end program kinds3

たかが、使うこともない金種計算プログラムだが、こうやって改良を加えていくと、小さなプログラムでも実用レベルまで持っていくのは、なかなか大変なことなのである。

今回はここまでにする。