第126回【Python】スーパースーパースーパーカー

Pythonby LeoSaki

現在取り組んでいるのは、paiza ラーニング問題集「クラス・構造体メニュー」になります。

学習サービストッププログラミング学習サイト【paizaラーニング】のメインページ。paiza独自のシステムにより、面倒な環境構築が不要、Webブラウザがあれば5秒で学習が開始できます。わかりやすい1本3分の動画と豊富な演習問題で初心者〜中級者のプログラミング学習を支援します。paiza.jp

はじめに

猫とキャンプと野球観戦と AWS が大好きな旦那、LeoSaki です。モフモフしたい。

Python をゼロから勉強してみよう、のコーナー 126 回目です。

田舎ですから、出勤時間はドア to ドアで 15 分くらいが助かります。そして、現在は 10 分くらいです。しかし、染みついたブラックは消えず、30 分前には着いてメールチェックを始めてしまいます。若手に指摘されてからは 20 分前に変更したのですが、たった 10 分が落ち着きません。

それでは、今日も頑張ってみようと思います。

スーパースーパースーパーカー

プログラミング練習問題「スーパースーパースーパーカー」問題を解いてスキルアップ!みんなも一緒に解いてみよう!paiza.jp

よくクラスの題材を扱う際に、「クラスは車の設計書」といった例が出てきます。
スーパーカー販売店に勤務しながらクラスの勉強をしていた paiza 君はスーパーカーの走る様子をクラスを用いてシミュレーションしてみようと考えました。
ただ車を走らせてもつまらないので、陸を走るスーパーカーに加えて、空を飛べるスーパースーパーカー ・ テレポートできるスーパースーパースーパーカー もシミュレーションに加えた
番号 1 〜 N の N 台のシミュレーションをすることにしました。

それぞれの車について、初めに入っている燃料の量 l と燃費 f が定まっており、加えて、車種に応じて次のような機能を持ちます。

・スーパーカー
run
燃料を 1 消費し、 f (km) 走る。
燃料が 0 の場合は何も起こらない。

・スーパースーパーカー
run
燃料を 1 消費し、 f (km) 走る。
燃料が 0 の場合は何も起こらない。

fly
燃料を 5 消費し、 f^2 (km) 飛行する。
燃料が 5 未満の場合は run を行う。

・スーパースーパースーパーカー
run
燃料を 1 消費し、 f (km) 走る。
燃料が 0 の場合は何も起こらない。

fly
燃料を 5 消費し、 2 * f^2 (km) 飛行する。
燃料が 5 未満の場合は run を行う。

teleport
燃料を f^2 消費し、 f^4 (km) 移動する。
燃料が f^2 未満の場合は fly を行う。

シミュレートする車の台数 N と機能を使う回数 K , N 台の車の車種と機能を使った車の番号と使った機能が与えられるので、全てのシミュレーションが終わった後の、各車ごとの総移動距離を求めてください。

N K
k_1 l_1 f_1
...
k_N l_N f_N
n_1 func_1
...
n_K func_K

・ 1 行目では、シミュレートする車の台数 N と機能を使う回数 K が半角スペース区切りで与えられます。
・ 続く N 行のうち i 行目(1 ≦ i ≦ N)では、 i 番の車の種類 k_i , 初めに入っている燃料 l_i , 燃費 f_i が半角スペース区切りで与えられます。
・ 続く K 行では、車の番号 n_i と、使用するその車の機能 func_i が時系列順に与えられます。


すべてのテストケースにおいて、以下の条件をみたします。

・ 与えられる値は全て整数
・ 1 ≦ N , K ≦ 10^5
・ k_i (1 ≦ i ≦ N) は “supercar”,”supersupercar”,”supersupersupercar” のいずれか
・ 1 ≦ l_i ≦ 10^5 , 1 ≦ f_i ≦ 100(1 ≦ i ≦ N)
・ 1 ≦ n_i ≦ N (1 ≦ i ≦ K)
・ func_i (1 ≦ i ≦ K) は “run” , “fly” , “teleport” のいずれか
・ 未定義の機能が呼び出されることはないことが保証されている


入力例

3 6
supercar 1 1
supersupercar 10 10
supersupersupercar 100 5
1 run
2 run
2 fly
3 run
3 fly
3 teleport

出力例

1
110
680

空を飛んだりテレポートしたり、やりたい放題のシミュレーションですな。こんなあり得ないものをシミュレーションするくらいなら、販売している車それぞれでシミュレーションしてみればいいのに。いや、paiza 国には、空を飛んだりテレポートしたりする車があるのかもしれない。

Python
class Supercar:
    def __init__(self,l,f):
        self.l = l
        self.f = f
        self.dist = 0
        
    def run(self):
        if self.l > 0:
            self.l -= 1
            self.dist += self.f
            
    def get_dist(self):
        return self.dist

class Supersupercar(Supercar):
    def __init__(self,l,f):
        super().__init__(l,f)
        
    def fly(self):
        if self.l < 5:
            super().run()
        else:
            self.l -= 5
            self.dist += self.f ** 2

class Supersupersupercar(Supersupercar,Supercar):
    def __init__(self,l,f):
        super().__init__(l,f)
    
    def fly(self):
        if self.l < 5:
            super().run()
        else:
            self.l -= 5
            self.dist += (self.f ** 2) * 2
        
    def teleport(self):
        if self.l < self.f ** 2:
            self.fly()
        else:
            self.l -= self.f ** 2
            self.dist += self.f ** 4
            
N,K = map(int,input().split())
cars = [None] * N

for i in range(N):
    s = input().split()
    car = s[0]
    l,f = int(s[1]),int(s[2])
    if car == 'supercar':
        cars[i] = Supercar(l,f)
    elif car == 'supersupercar':
        cars[i] = Supersupercar(l,f)
    elif car == 'supersupersupercar':
        cars[i] = Supersupersupercar(l,f)

for i in range(K):
    s = input().split()
    idx = int(s[0]) - 1
    func = s[1]
    if func == 'run':
        cars[idx].run()
    elif func == 'fly':
        cars[idx].fly()
    elif func == 'teleport':
        cars[idx].teleport()

for car in cars:
    print(car.get_dist())

多重継承の際の解決順序に注意。

class Supersupersupercar(Supercar,Supersupercar):

こう書くと、MRO エラーが発生する。ちょっと時間を取られちゃったので、ここで共有。そして、反省。

VBA
## Supercar
Private l As Long
Private f As Integer
Private dist As Long

Property Let car(li, fu)

    l = li
    f = fu
    dist = 0
    
End Property

Function run()

    If l > 0 Then
        l = l - 1
        dist = dist + f
    End If
    
End Function

Function fly()

End Function

Function teleport()

End Function

Property Get getDist()

    getDist = dist
    
End Property
## Supersupercar
Implements Supercar

Private l As Long
Private f As Integer
Private dist As Long

Property Let Supercar_car(li, fu)

    l = li
    f = fu
    dist = 0
    
End Property

Function Supercar_run()

    If l > 0 Then
        l = l - 1
        dist = dist + f
    End If
    
End Function

Function Supercar_fly()

    If l < 5 Then
        Me.Supercar_run
    Else
        l = l - 5
        dist = dist + f ^ 2
    End If
    
End Function

Function Supercar_teleport()

End Function

Property Get Supercar_getDist()

    Supercar_getDist = dist
    
End Property
## Supersupersupercar
Implements Supercar

Private l As Long
Private f As Integer
Private dist As Long

Property Let Supercar_car(li, fu)

    l = li
    f = fu
    dist = 0
    
End Property

Function Supercar_run()

    If l > 0 Then
        l = l - 1
        dist = dist + f
    End If
    
End Function

Function Supercar_fly()

    If l < 5 Then
        Me.Supercar_run
    Else
        l = l - 5
        dist = dist + (f ^ 2) * 2
    End If
    
End Function

Function Supercar_teleport()

    If l < f ^ 2 Then
        Me.Supercar_fly
    Else
        l = l - f ^ 2
        dist = dist + f ^ 4
    End If
    
End Function

Property Get Supercar_getDist()

    Supercar_getDist = dist
    
End Property
## 標準モジュール
Sub class_primer__super_super_supercar()

    NK = Split(Cells(1, 1), " ")
    N = Val(NK(0))
    K = Val(NK(1))
    
    Dim cars() As New Supercar
    ReDim cars(N - 1)
    
    For i = 0 To N - 1
        s = Split(Cells(i + 2, 1), " ")
        car = s(0)
        li = Val(s(1))
        fu = Val(s(2))
        If car = "supercar" Then
            Set cars(i) = New Supercar
        ElseIf car = "supersupercar" Then
            Set cars(i) = New Supersupercar
        ElseIf car = "supersupersupercar" Then
            Set cars(i) = New Supersupersupercar
        End If
        cars(i).car(li) = fu
    Next
    
    For i = 1 To K
        s = Split(Cells(i + N + 1, 1), " ")
        idx = Val(s(0)) - 1
        func = s(1)
        If func = "run" Then
            cars(idx).run
        ElseIf func = "fly" Then
            cars(idx).fly
        ElseIf func = "teleport" Then
            cars(idx).teleport
        End If
    Next
    
    For i = LBound(cars) To UBound(cars)
        Debug.Print cars(i).getDist
    Next
    
End Sub

継承の継承の仕方が結局判明せず。しかし、VBA の継承の書き方的に、継承の継承はできないのではないだろうか。ということで、親クラスに空の Function を並べて、継承先で料理することにした。今回はこれで済んだけれど、多重継承の場合は、もっと複雑に親クラスをわける必要があるような気がしている。

最後に

Python のコードは数分で書き終わった。そして、これを VBA で書くにはどうしたら良いかと頭を抱えた。ちょうどこの日、仕事が忙しかったこともあり、勉強を始める時間が遅かったため、初の日を跨いでの挑戦になってしまった。布団に入ってからも、何か方法は何か方法は~と、なかなか寝付けなかった。

VBA は元々継承をサポートしていないということで、これが本当にあっているのかは謎。しかし、逆に、ここで頭を悩ませたことで Python のクラスについても更に学習が進んだ気がする。オブジェクト指向って感じで面白いでしょ?

引き続き、よろしくお願いいたします!

LeoSaki

旦那の方。 猫と嫁とのキャンプをこよなく愛する。 AWS 6 冠達成者。AWS 好き。 ITパスポート、基本情報技術者、応用情報技術者も取得しています。