理系母の趣味はプログラミング

理系院卒、メーカー技術職の二児の母が、PythonやVBAで色々と作ってアップしていくブログです。

【VBA】系統図作成マクロを作ってみた

お久しぶりです。
安定期に入り、つわりも落ち着き、プログラミングの勉強を再開しました。
職場復帰した時に役に立つもの作れないかな~と思い、
職場でよく使っていた系統図をもっと簡単に作れればいいなーと、ご無沙汰だったエクセルVBAをいじいじ。
系統図は新QC7つ道具の一つで、問題を解決すべき最適な手段や方策を系統的に追求する方法です。
本来の用途とはちょっとずれますが、問題や課題を具体的な要素まで抜け漏れなく分解するのにも役に立っていました。

作ったマクロでできるのはこんな図。

f:id:kanafuu:20201003230853p:plain
系統図例

子を追加したいとき、親枠を選択した状態で「追加マクロ」を実行すると、枠とコネクタ線が追加されます。
削除したい枠を選択した状態で「削除マクロ」を実行すると、枠と不要になったコネクタ線が削除されます。他の枠は、それに伴いスライドされます。
「次数出力マクロ」は、ただ設定した次数を出力するだけのおまけです。
あ、でも系統図の次数はI1セルに入力しておかないと、削除マクロがバグります。
なんでかというと、どこからどこまで探索すればいいのかをI1セルに入力されている次数で判断しているからです。
もっとうまくできる気がするんですけど、体力がないのでやる気がでたらやります。申し訳ないです。


今回エクセルファイルをアップできればよかったのですが、
できないみたいなので、申し訳ないですが、
使う際は下記のようにフォーマットを整える必要があります。

f:id:kanafuu:20201003234223p:plain
マクロを使うための下準備

【必須準備】
①目的、目標を入力する枠を作成する(図中①)。セルを二行結合して太枠で囲む。
②次数(系統図のうち目的・目標の列を除いた列の総数)を入力するセルを"I1"にする(図中②)

【任意準備】
追加や削除など、マクロを実行する際に使うボタンを作成しておく。
ボタン以外に、ショートカットキーを使ってもなんでもいいです。

以下VBAコード3つ。

「追加マクロ」

Sub 追加()
    Dim firstCell, newCell, lineCell As Range
    Dim endCol As Integer
    Dim i, j, a As Integer
    Dim firstrow, firstcol As String
    
    Set firstCell = Selection '選択セル
    firstrow = firstCell.Row '削除するセルの行番号
    firstcol = firstCell.Column '削除するセルの列番号
    
    '親要素を選択していない場合マクロ終了
    If firstCell.Borders(xlEdgeTop).LineStyle <> 1 Then '上枠判定だからいまいちだけど無いよりまし
        MsgBox "追加する要素の親を選択してからクリックしてね"
        Exit Sub
    End If
    
    '選択したセルの右3つめのセルにすでに枠が作成されていたら、
    If firstCell.Offset(0, 3).Borders(xlEdgeTop).LineStyle = xlContinuous Then
    '選択セルから分岐するセルの内、最も下に位置する枠の行数を取得し、その行の下に3つ行を追加して枠を作る。
        
        i = 1
        j = 1
                    
        Do Until Cells(firstrow + i, firstcol + j).Borders(xlEdgeRight).LineStyle = xlLineStyleNone And _
        Cells(firstrow + i, firstcol + j + 3).Borders(xlEdgeTop).LineStyle = xlLineStyleNone
            If Cells(firstrow + i, firstcol + j).Borders(xlEdgeRight).LineStyle = xlContinuous Then
                i = i + 3
            ElseIf Cells(firstrow + i, firstcol + j + 3).Borders(xlEdgeTop).LineStyle = xlContinuous Then
                j = j + 3
            End If
        Loop
        
        Rows(firstrow + i + 1 & ":" & firstrow + i + 3).Insert 'newCell定義する前に挿入しないとおかしなことになった
        
        Set newCell = firstCell.Offset(i + 1, 3).Resize(2, 1)
        
    'firstCellの右三つ目のセルに枠がない場合、そのまま横に作成する。
    Else
        Set newCell = firstCell.Offset(0, 3).Resize(2, 1)
    
    End If
        
        newCell.Merge
        newCell.BorderAround Weight:=xlMedium
    
    '以下接続線作成部分
    'newCellとfirstCellが同じ行の場合は横に一本引く
    If firstCell.Row = newCell.Row Then
        Set lineCell = newCell.Offset(0, -2).Resize(1, 2)
        lineCell.Borders(xlEdgeBottom).LineStyle = xlContinuous
        
    'そうでない場合はfirstCellからnewCell行までの縦線と、横線を引く
    Else
        Set lineCell = Range(Cells(firstCell.Row + 1, firstCell.Column + 2), Cells(newCell.Row, newCell.Column - 1))
        lineCell.Borders(xlEdgeLeft).LineStyle = xlContinuous
        lineCell.Borders(xlEdgeBottom).LineStyle = xlContinuous
    
    End If

End Sub


「削除マクロ」

Sub 削除()

Dim DelCell As Range
Dim i, j, n, a, b As Long
Dim ad, delrow, delcol, botrow As String

Set DelCell = Selection '選択セル=削除するセル
delrow = DelCell.Row '削除するセルの行番号
delcol = DelCell.Column '削除するセルの列番号
    
    If DelCell.Borders(xlEdgeTop).LineStyle <> 1 Then '上枠判定だからいまいちだけど無いよりまし
        MsgBox "削除したい要素を選択してからクリックしてね"
        Exit Sub
    End If
    
    '選択したセルの右3つめのセルにすでに枠が作成されていたら(=選択セルが末端でなかったら)
    If DelCell.Offset(0, 3).Borders(xlEdgeTop).LineStyle = xlContinuous Then
        MsgBox "末端要素以外は消せません"
        Exit Sub
    End If

    '同じ行の左に親枠があり、同列下にも枠がある場合、そこから3行と右側を削除して上にシフト
    'そして繋がっていた一番下のセルがあった場所を3行削除
    
    
    a = 5 + 3 * (Range("I1").Value - 1) ' 系統図の右端列数
    b = DelCell.Offset(0, -1).Column
    
    
    If Cells(delrow, delcol - 3).Borders(xlEdgeTop).LineStyle = xlContinuous Then
        If Cells(delrow + 1, delcol - 1).Borders(xlEdgeLeft).LineStyle = xlContinuous Then
        
            i = 0
            n = 1


            Do
                i = i + 3
                Cells(delrow + i, delcol - 1).Resize(2, a - b + 1).Select 'デバッグ用
                Cells(delrow + i - 3, delcol - 1).Select 'デバッグ用
                Cells(delrow + i, delcol - 1).Resize(2, a - b + 1).Cut Cells(delrow + i - 3, delcol - 1) '上にカット&ペースト
                
            Loop Until Cells(delrow + i + 1, delcol - 1).Borders(xlEdgeLeft).LineStyle = xlLineStyleNone
            
            botrow = delrow + i - 1 '削除する行上端(ここから3行分、系統図列分削除)
            '罫線を消す
            Cells(delrow, delcol - 1).Borders(xlEdgeLeft).LineStyle = xlLineStyleNone
            '左端から三行削除
            Range(Cells(botrow, 1), Cells(botrow + 2, a)).Delete (xlShiftUp)
        
        Else
            Cells(delrow, delcol - 1).Resize(2, 3).Borders.LineStyle = xlLineStyleNone '左に枠あるけど下に付随する枠ないときはシンプルに枠消すだけ
            Cells(delrow, delcol - 2).Borders(xlEdgeBottom).LineStyle = xlLineStyleNone
        End If
    ElseIf Cells(delrow + 1, delcol - 1).Borders(xlEdgeLeft).LineStyle = xlContinuous Then '中間にある枠の場合
        '三行削除
        Range(Cells(delrow, 1), Cells(delrow + 2, a)).Delete (xlShiftUp)
    Else '最下枠の場合
        i = 0
        Do
            i = i - 1
            DelCell.Offset(i, -1).Borders(xlEdgeLeft).LineStyle = xlLineStyleNone
        Loop Until DelCell.Offset(i, -1).Borders(xlEdgeTop).LineStyle = xlContinuous
        delrow = DelCell.Row '削除する行上端(ここから3行分、系統図列分削除)
        Range(Cells(delrow, 1), Cells(delrow + 2, a)).Delete (xlShiftUp)
    End If
            
End Sub

「次数出力マクロ」

Sub 次数出力()

Dim num, a, i As Long
 

num = Range("I1").Value

    a = 1
    Do While InStr(Cells(3, 3 * a + 2).Value, "次") > 0
        Cells(3, 3 * a + 2).Clear
        a = a + 1
    Loop


    For i = 1 To num
        Cells(3, 3 * i + 2).Value = i & "次"
    Next i

End Sub

コードは以上です。
久しぶりのVBAは疲れました~。
思い出しながらなので、やたらコメントが多いですね。
改善の余地はありそうですが、気が向いたらじっくり考えます。
とりあえず、だいぶ楽になったので良いよね!

もっとこうしてほしいとか、何かあればコメントください。

以上です。

【雑記】二人目妊娠しました。

ご無沙汰しております。
しばらくブログの更新ストップしてました。
その理由は
二人目妊娠によるつわり!眠気!
妊娠前のやる気がうそのよう。
もう何もする気が起きず、
1歳の娘の育児だけで精一杯。
勉強なんて無理~状態です。
つわりも少し収まってきて、ようやく娘の昼寝中に行動するようになりまして、
久しぶりにブログを更新しています。
(今までは常に気持ち悪くて、娘と一緒に寝る生活でした…つまり自分時間皆無)
つわりが落ち着いてきたとはいえ、妊娠前に比べるとだるいし眠いしで、
当分プログラミングの勉強は中断すると思います…。情けねえ…だけど無理なもんは無理…。

ということで、元気が出てきたらこのブログも再開します。

ではでは!

基本情報技術者アルゴリズムの問題をPythonでコーディングしてみた【平成28年春】

過去問コーディング。
今回の問題は、簡易メモ帳プログラムです。

過去問PDF「出典:平成28年度 春期 基本情報技術者試験 午後 問8」

プログラム

#グローバル変数を定義
MemoCnt = 0
MemoMax = 5
Memo = ['']*MemoMax #リストが空だと要素番号で値を入れることがないため、枠を作っておく
DataLen = 0
DataMax = 25
Data = ['']*DataMax #枠を作っておく


def reset_memo():
    global MemoCnt, DataLen
    MemoCnt = 0
    DataLen = 0

def add_memo(text):
    global MemoCnt,Memo,DataLen,Data

    textLen = len(text)
    Memo[MemoCnt] = DataLen
    MemoCnt = MemoCnt + 1
    Data[DataLen] = textLen
    DataLen = DataLen + 1
    for i in range(textLen):
        Data[DataLen + i] = text[i]

    DataLen = DataLen + textLen

def delete_memo(pos):
    global MemoCnt,Memo

    i = pos+1
    while i < MemoCnt:
        Memo[i - 1] = Memo[i]
        i = i + 1

    MemoCnt = MemoCnt - 1

def change_memo(pos,text):
    global Memo,DataLen,Data

    textLen = len(text)
    Memo[pos] = DataLen
    Data[DataLen] = textLen
    DataLen = DataLen + 1
    for i in range(textLen):
        Data[DataLen + i] = text[i]

    DataLen = DataLen + textLen

def move_memo(fromPos,toPos):
    global Memo

    m = Memo[fromPos]
    if fromPos < toPos:
        for i in range(fromPos,toPos):
            Memo[i] = Memo[i+1]

    if fromPos > toPos:
        for i in range(fromPos,toPos,-1):
            #toPos+1→toPosに変更。range(a,b)はa=<i<bのため
            Memo[i] = Memo[i-1]

    Memo[toPos] = m

def print_ans():
    print(Memo)
    print(Data)
    print(MemoCnt)
    print(DataLen)

def clear_garbage():
    global Memo,DataLen,Data

    temp = ['']*DataMax

    DataLen = 0
    
    if MemoCnt == 0:
        return

    for m in range(MemoCnt):
        d = Memo[m]
        Memo[m] = DataLen
        for i in range(Data[d]+1): #Data[d]→Data[d]+1に変更
            temp[DataLen] = Data[d+i]
            DataLen = DataLen + 1

    for d in range(DataLen):
        Data[d] = temp[d]



print('実行例1')
reset_memo()
print_ans()

print('実行例2')
add_memo('Aoki')
add_memo('Imai')
add_memo('Uno')
add_memo('Endo')
print_ans()

print('実行例3')
delete_memo(0)
print_ans()

print('実行例4')
change_memo(2,'Abe')
print_ans()

print('実行例5')
move_memo(2,0)
print_ans()

print('設問2')
clear_garbage()
print_ans()

出力結果

実行例1
['', '', '', '', '']
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
0
0
実行例2
[0, 5, 10, 14, '']
[4, 'A', 'o', 'k', 'i', 4, 'I', 'm', 'a', 'i', 3, 'U', 'n', 'o', 4, 'E', 'n', 'd', 'o', '', '', '', '', '', '']
4
19
実行例3
[5, 10, 14, 14, '']
[4, 'A', 'o', 'k', 'i', 4, 'I', 'm', 'a', 'i', 3, 'U', 'n', 'o', 4, 'E', 'n', 'd', 'o', '', '', '', '', '', '']
3
19
実行例4
[5, 10, 19, 14, '']
[4, 'A', 'o', 'k', 'i', 4, 'I', 'm', 'a', 'i', 3, 'U', 'n', 'o', 4, 'E', 'n', 'd', 'o', 3, 'A', 'b', 'e', '', '']
3
23
実行例5
[19, 5, 10, 14, '']
[4, 'A', 'o', 'k', 'i', 4, 'I', 'm', 'a', 'i', 3, 'U', 'n', 'o', 4, 'E', 'n', 'd', 'o', 3, 'A', 'b', 'e', '', '']
3
23
設問2
[0, 4, 9, 14, '']
[3, 'A', 'b', 'e', 4, 'I', 'm', 'a', 'i', 3, 'U', 'n', 'o', 'o', 4, 'E', 'n', 'd', 'o', 3, 'A', 'b', 'e', '', '']
3
13

【原文コードとの主な相違点】
・リスト型の変数について、あらかじめ枠を作るためのコードを追加。
・for ループの終値
【補足】
・設問2の出力結果を見ると、clear_garbage()実行後もData[13~22]の値が残っている。実は原文のプログラムでも同様にDataに残る。これでは一見、空き要素が増やせてないじゃん!と思われるが、DataLen=13になることで、メモとして参照されるのはData[0]~Data[12]までとなり、Data[13]~Data[22]は’空き要素’扱いになる。したがって、次にadd_memo()を実行すると、Data[13~]に上書きされる。

【感想】
これもほぼ問題文どおりです。
問題文通り過ぎて、プログラムをコピーしているだけの作業になってきた、、、!
基本情報の勉強になってるのか疑問ですね。
こればっかりやっていても基本情報の勉強効果は薄れ気味だし、
pythonの習得にも偏りがありそうなので、
他にもやること考えます。

pythonの文法としては、
毎回変数のスコープでつまずきます。
関数の中でグローバルな変数を書き換えたいときに
'global 変数'のコードを忘れていて、
「UnboundLocalError: local variable 'DataLen' referenced before assignment」
みたいなことをpython先輩に言われてしまう。
にしても関数を書くときに毎回、global って定義するの面倒だなーと思います。
関数の中からグローバル変数を書き換えたい場合の、もっといい方法ないのかしら。
知ってるよって先生方いらっしゃいましたら教えてください。


以上です。
それではまた^^

基本情報技術者アルゴリズムの問題をPythonでコーディングしてみた【平成28年秋】

過去問コーディング。
今回の問題は、数値の編集プログラムです。

過去問PDF「出典:平成28年度 秋期 基本情報技術者試験 午後 問8」

プログラム

def edit(Pattern,Value):

    #上書きするために、リスト型のPatternを作っておく
    Pattern_l = list(Pattern)
    
    fill = Pattern[0]
    signif = 'off'
    v = 0
    for p in range(0,len(Pattern)):
        if Pattern[p]=='▯' or Pattern[p]=='▮':  #ケース1-7の処理
            if signif == 'off':                 # signif = offの場合の処理
                if Pattern[p] =='▯' and Value[v]=='0':
                    pass                        #何もしない ケース1
                else:
                    if Value[v+1] != '+':
                        signif = 'on'           #ケース2,4
                if Value[v] == '0':
                    Pattern_l[p] = fill         #ケース1,2,3
                else:
                    Pattern_l[p] = Value[v]     #ケース4,5
            else:                               #signif = onの場合の処理
                if Value[v+1] == '+':
                    signif = 'off'              #ケース7
                Pattern_l[p] = Value[v]         #ケース6,7
            v = v + 1

        else:                                   #ケース8,9の処理
            if signif == 'off':                 #ケース8
                Pattern_l[p] = fill             #ケース8
            else:
                pass                            #ケース9 何もしない(追加)

    Pattern = "".join(Pattern_l)                #リストを文字列に戻す
    return Pattern


print('例: ' + edit('__▯▯,▯▯▯','01234+'))
print('設問1-a: '+ edit('*▯▯,▯▯▯#','00000+'))
print('設問1-b: '+ edit('*▯▯▯.▯▯#','00012-'))
print('設問1-c: '+ edit('*▯▯▮.▯▯#','00012+'))

出力結果

例: ___1,234
設問1-a: ********
設問1-b: *****12#
設問1-c: ****.12*

【過去問との主な相違点】
・Pattern文字列の上書きをするために、リスト型のPatternを新たに作った。
Pythonの文字列はイミュータブル(変更不可)のため文字列のままだと上書きできない。リストはミュータブル。)

【感想】
ほぼ問題文通りです。
ifの入れ子が多すぎてごちゃごちゃしてますが、問題文を忠実に再現した結果ということで…。
elseのあとにifってelifと同等ですか?だれか教えて下さい。ピヨピヨ。
あとこの問題、トレースがしんどいですね!
ルールに当てはめるのがまあめんどい。
時間かかるし、なんか焦るし。
このルールを理解するのがこの問題のカギっぽいですね、この設問3とか。難し。

本番もこういう問題でたらまずいなと思うのでした。


最近はPythonでDeepLearningの勉強してます。
FEから横道それてます。はい。

おもしろいけど、そっちは難しすぎて記事にはできそうにないです。

いつかここに、「Pythonで今日の献立を提案してくれるAI作ってみた」とかアップしてみたいものです。^^

それではまた^^

基本情報技術者アルゴリズムの問題をPythonでコーディングしてみた【平成29年春】

過去問コーディング。
今回の問題は、最短経路と最短距離の探索プログラムです。

過去問PDF「出典:平成29年度 春期 基本情報技術者試験 午後 問8」

プログラム

#配列Distanceを作成する(愚直に作ったけどもっといい方法ありそう)
Distance = [[0,2,8,4,-1,-1,-1],
            [2,0,-1,-1,3,-1,-1],
            [8,-1,0,-1,2,3,-1],
            [4,-1,-1,0,-1,8,-1],
            [-1,3,2,-1,0,-1,9],
            [-1,-1,3,8,-1,0,3],
            [-1,-1,-1,-1,9,3,0]]

def shortest_path(Distance,sp,dp):
    nPoint = len(Distance)

    #初期化
    sDist = float('inf') #出発地から目的地までの最短距離に初期値(∞)を格納する
    sRoute = [-1] * nPoint #最短経路上の地点の地点番号に初期値を格納する
    pRoute = [0] * nPoint #初期値格納(問題文にはなかったため追加)
    pDist = [float('inf')] * nPoint #出発地から各地点までの最短距離に初期値を格納する
    pFixed = [False] * nPoint #各地点の最短距離の確定状態に初期値を格納する

    pDist[sp] = 0 #出発地から出発地自体への最短距離に初期値を格納する

    while True:
        i = 0
        while i < nPoint: #未確定の地点を一つ探す
            if not(pFixed[i]):
                break #最内側の繰り返しから抜ける
            i = i + 1
        if i == nPoint: #出発地からすべての地点までの最短距離が確定
            break #していれば、最短経路探索処理を抜ける
        for j in range(i+1,nPoint): #最短距離がより短い地点を探す
            if not(pFixed[j]) and pDist[j] < pDist[i]:
                i = j

        sPoint = i
        print('sPoint='+ str(i))
        pFixed[sPoint] = True #出発地からの最短距離を確定する

        for j in range(0,nPoint):
            if Distance[sPoint][j] > 0 and not(pFixed[j]):
                newDist = pDist[sPoint] + Distance[sPoint][j]
                if newDist < pDist[j]:
                    pDist[j] = newDist
                    pRoute[j]  = sPoint
        print(pDist,pRoute)

    sDist = pDist[dp]
    j = 0
    i = dp
    
    while i != sp:
        sRoute[j] = i
        i = pRoute[i]
        j = j + 1

    sRoute[j] = sp

    print('最短経路の地点番号:'+str(sRoute))
    print('最短距離'+str(sDist))

shortest_path(Distance,0,6)

出力結果

sPoint=0
[0, 2, 8, 4, inf, inf, inf] [0, 0, 0, 0, 0, 0, 0]
sPoint=1
[0, 2, 8, 4, 5, inf, inf] [0, 0, 0, 0, 1, 0, 0]
sPoint=3
[0, 2, 8, 4, 5, 12, inf] [0, 0, 0, 0, 1, 3, 0]
sPoint=4
[0, 2, 7, 4, 5, 12, 14] [0, 0, 4, 0, 1, 3, 4]
sPoint=2
[0, 2, 7, 4, 5, 10, 14] [0, 0, 4, 0, 1, 2, 4]
sPoint=5
[0, 2, 7, 4, 5, 10, 13] [0, 0, 4, 0, 1, 2, 5]
sPoint=6
[0, 2, 7, 4, 5, 10, 13] [0, 0, 4, 0, 1, 2, 5]
最短経路の地点番号:[6, 5, 2, 4, 1, 0, -1]
最短距離13

↑設問解答用にいろいろ出力してます。

【過去問との主な相違点】
・配列Distanceを作成するためのコードを追加
・pRouteの初期化を追加
・「∞」はpythonではfloat('inf')

【感想】
ほぼ過去問のままでできました~

基本情報技術者試験の勉強も兼ねているこのコーディング。
今回は、問題文をそのままpythonのコードに直すだけでスラスラーと実行できてしまったので、
コード書いても、アルゴリズムの中身ちゃんと理解できませんでしたTT
やっぱり基本情報技術者アルゴリズムって難しい~。

さて、
これからは忘れかけてる午前問題の勉強と、
午後問のコーディングと、
pythonの勉強を
その日の気分で勉強していこうと思います。

それではまた^^

基本情報技術者アルゴリズムの問題をPythonでコーディングしてみた【平成29年秋】

過去問コーディング。
今回の問題は、文字列の誤りを検出するプログラムです。

過去問PDF「出典:平成29年度 秋期 基本情報技術者試験 午後 問8」

プログラム

#表1の文字と数値の辞書を生成
char_dict = {'_':0,'.':1,',':2,'?':3}
for i,c in enumerate(range(ord('a'),ord('z')+1)):
    char_dict[chr(c)] = i + 4

#関数getValueを定義   
def get_value(char):
    return char_dict[char]

#関数getCharを定義
def get_char(val):
    keys = [k for k, v in char_dict.items() if v == val]
    if keys:
        return keys[0]
    return None
    
def calc_check_character(input):

    l = len(input)

    N = 30
    sum = 0
    is_even = False
    
    for i in range(l,0,-1):
        value = get_value(input[i-1])#インデックスが0始まりの為
        if is_even == True:
            sum = sum + value
        else:
            sum = sum + (value * 2) // N + (value * 2) % N
        is_even = not is_even
        
    check_value = (N - sum % N) % N
    return get_char(check_value)

def validate_check_character(input):

    l = len(input)
    
    N = 30
    sum = 0
    is_odd = True
    ret_value  = True
    
    for i in range(l,0,-1):
        value = get_value(input[i-1])#インデックスが0始まりの為
        if is_odd == True:
            sum = sum + value
        else:
            sum = sum + (value * 2) // N + (value * 2) % N
        is_odd = not is_odd

    if sum % N != 0:
        ret_value = False

    return ret_value

#検査文字付文字列の生成例
print(calc_check_character('ipa__'))

#設問2
print(validate_check_character('ipb__f'))
print(validate_check_character('api__f'))
print(validate_check_character('pia__f'))
print(validate_check_character('__apif'))

#設問3
print(calc_check_character('_s__'))

出力結果

f
False
True
False
True
l

【過去問との主な相違点】
・文字と数値の辞書を作成するコードを追加
・関数getValueとgetCharを定義するコードを追加
pythonのインデックスは0始まりのため、変数inputを参照するインデックス値をi-1とした
・÷→//, ≠→!=

【感想】
これもほぼ過去問通りですが、文字と数値の辞書を作成するコードと関数getCharがわからず、ググってコピペしました。
辞書のデータ型って、キーを参照してバリューを取得するのは簡単なのに、逆は少し手間がかかるのね。
また、今回の過去問、説明文からプログラムの機能を想像するのが難しかったです。
文字列の誤り検出ってのも、どんな場面で役に立つのか、よくわからないし、
割り算処理を繰り返して、判定できるロジックも、よくわからず。
このアルゴリズムを証明してる人いたら教えてほしい!

そろそろ基本情報技術者の勉強ちゃんと再開しないとな~

それでは、また^^

基本情報技術者アルゴリズムの問題をPythonでコーディングしてみた【平成30年秋】

過去問コーディング。
今回の問題は、整数式の解析と計算のアルゴリズムです。

過去問PDF「出典:平成30年度 秋期 基本情報技術者試験 午後 問8」

プログラム

def compute(Expression):

    ExpLen = len(Expression)

    #プログラム(解析処理の部分)

    Operator =[]
    Priority = []
    OpCnt = 0
    Value = [0]
    nest = 0


    for i in range(0,ExpLen):
        
        chr = Expression[i]
        
        if ('0' <= chr) and (chr <= '9'): #数字0~9か?
            Value[OpCnt] = 10 * Value[OpCnt]+int(chr)

        if (chr == '+') or (chr == '-') or (chr == '*') or (chr == '/'):
            Operator.append(chr)  #変更点:要素番号で指定すると、番号の枠がない場合エラーになる
            
            if (chr == '+') or (chr == '-'):
                Priority.append(nest + 1) #変更点
            else:
                Priority.append(nest + 2) #変更点

            OpCnt = OpCnt + 1
            Value.append(0) #変更点:次Valueに数字を入れるために枠を作っておく
            
        if chr == '(':
            nest = nest + 10
        if chr == ')':
            nest = nest - 10
            
    #確認用
    print('確認用:'+ Expression,Value,Operator,Priority)

    #プログラム(計算処理の部分)

    while OpCnt > 0:

        #優先度の最も高い要素番号を取得
        ip = 0
        for i in range(1,OpCnt):
            if Priority[ip] < Priority[i]:
                ip = i
                
        chr = Operator[ip]

        #計算
        if chr == '+':
            Value[ip] = Value[ip] + Value[ip + 1]

        if chr == '-':
            Value[ip] = Value[ip] - Value[ip + 1]

        if chr == '*':
            Value[ip] = Value[ip] * Value[ip + 1]

        if chr == '/':
            Value[ip] = Value[ip] / Value[ip + 1]

        #各リストの要素を左詰めにする
        for i in range(ip+1,OpCnt):
            Value[i] = Value[i+1]
            Operator[i-1] = Operator[i]
            Priority[i-1] = Priority[i]

        OpCnt = OpCnt - 1

    print(Value[0])

compute('2*(34-(5+67)/8)')
compute('2*(-1)')

出力結果

確認用:2*(34-(5+67)/8) [2, 34, 5, 67, 8] ['*', '-', '+', '/'] [2, 11, 21, 12]
50.0
確認用:2*(-1) [2, 0, 1] ['*', '-'] [2, 11]
-2

過去問との主な相違点は下記
・プログラム中に全角を入れるのが嫌なので、演算子は半角に変更
・問題では要素番号100までのリストを定義しているが、出力が膨大になるのが嫌なので、appendで要素を追加する方法を採用
・リスト追加の方法をインデックス指定からappendに変更(Pythonの仕様上)

【感想】
シンプルなコードだったので、ほぼ過去問の記述通りでいけました。
ただ、「計算式を入力すると計算結果がでる」今回のプログラム、
やりがいはありませんでした(笑)
だって計算なんて、Pythonに入力すれば普通に出してくれるんだもの。

まあでも、カッコとか演算子の優先順位がある場合、コンピュータの中ではどんな手順で処理しているのか
そんな裏側のことを想像できてためになったのかな!

以上です!

それではまた^^