質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.35%

  • Python 3.x

    7338questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

もっと簡潔に書きたいpython3<no.1>

解決済

回答 7

投稿

  • 評価
  • クリップ 0
  • VIEW 332

hmori

score 4

 前提・実現したいこと<no.1>

簡潔にもっと綺麗に作りたい
よろしければご指導してください

python3 で最近プログラミングを勉強しています

標準入力で
最初に投げた回数(N)
例1 として5回
ball  ⇒ ball!
ball   ⇒ ball!
strike ⇒ ball!
strike ⇒ ball!
(strike) ⇒  out!
のような動きにしています

例2
最初に投げた回数(N)
例2 として4回
ball ⇒ ball!
ball ⇒ ball!
strike ⇒ strike! 
ball ⇒ fourball!

もっと簡潔に綺麗に作れるのではないかと思いますが
初心者の為、冗長になってしまっています

 該当のソースコード

N = int(input())
s = 0
b = 0
i = 0

while i <= N-1:
    a = input()
    if  a == 'ball':
        b = b + 1
        if  b < 4:
            print('ball!')
        else:
            print('fourball!')
            break
    if  a == 'strike':
        s = s + 1
        if  s < 3:
            print('strike!')
        else:
            print('out!')
            break
    i = i+1

 補足情報(FW/ツールのバージョンなど)

ブラウザでプログラムが書けるpaiza.ioのpython3
を使用して書いています
以上、よろしくお願いします

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 7

+5

変数の数を少なくする方針で書いてみました。

N = int(input())
records = []

for i in range(0, N):
    records.append(input())

    if records.count("ball") == 4:
        print("forball!")
        break
    elif records.count("strike") == 3:
        print("out!")
        break
    else:
        print("{}!".format(records[-1]))

質問文にある出力例とは動作は違っています。野球の投球判定のルールに基づいて書いています。
strike, ball 以外が入力されたときのことは考慮していません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/26 23:12

    ありがとうございます
    読んでこういう風にかけたらいいなと思いました
    読むことはできるけど自分で作り出すのが難しいです
    大変助かりました

    キャンセル

+4

# -*- coding: utf-8 -*-
from collections import Counter
score  = Counter()
outs = {'strike': (3, 'out!'), 'ball': (4, 'fourball!')}

for _ in range(int(input())):
    a = input()
    score[a] += 1
    end_count, msg = outs[a]
    if score[a] == end_count:
        print(msg)
        break

    print(a + '!')


◇参考情報

  1. 4.6.5. タプル型 (tuple)
  2. 4.10. マッピング型 — dict
  3. 8.3.2. Counter オブジェクト

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/26 23:26

    回答ありがとうございます
    Counterを使っているのですね
    すっきりしてみやすいです
    大変助かりました

    キャンセル

checkベストアンサー

+2

冗長と感じる典型的な要因に「同じようなパターンが繰り返されている」というのがあると思います。

どの言語でも同じですが「同じパターンものはまとめて処理」するとスッキリすることがあります。例えばキーが0, 1, ...の情報があってそれをキーの値に応じてどれかをインクリメントするとき

if key == 0:
    v0 += 1
elif key == 1:
    v1 += 1
elif ...
    ...


こういう書き方はしたくないと感じます。プログラマーは誰もが「怠惰」なので特に同じことの繰り返しは見た瞬間「ヤダー」と感じますな。このようなパターンはキー(配列の指標もキーの一種と言えます)によって異なる値へアクセスできるlist, dict, etc.を使うのが常道の一つです。上の例でいえば

v[key] += 1

で済みますよね?これを本件に適用するなら

ball_or_strike = input()
count[ball_or_strike] += 1

と書ければボールとストライク両方のカウントができますのでdictが便利でしょう。カウントの上限や表示するメッセージが違うという話も同じ考え方で扱うことが可能です。

リスト1

count = dict(ball=0, stroke=0)
limit = dict(ball=4, stroke=3)
settlement = dict(ball='walk', stroke='out')
while True:
    bos = input()
    if bos in count:
        count[bos] += 1
        finished = count[bos] >= limit[bos]
        print(settlement[bos] if finished else bos)
        if finished:
            break

また上ではball, strikeごとに3つの情報を取り扱ってますがそれぞれを別々の変数にしているのが煩雑と感じることもあります。3つならまだしも4つ5つ...と増えていくと色んな変数があちこちにでてきてコードが捉えにくくなる原因になると思います。これは同じコードを複数個所に書いているかどうかという話とは別ですね。こうした場合「あるひとまとまりの情報をなにかの構造データに閉じ込める」なんてことをするとスッキリすることがあります。

リスト2

states = dict(
    ball=dict(count=0, limit=4, settlement='walk'),
    strike=dict(count=0, limit=3, settlement='out'),
)

while True:
    bos = input()
    if bos in states:
        s = states[bos]
        s['count'] += 1
        finished = s['count'] >= s['limit']
        print(s['settlement'] if finished else bos)
        if finished:
            break


リスト1で出てくる変数は5, リスト2では4なのでこの例では違いは大差ないと感じられますがcount, limit, settlementといった情報が増えてくると変数の数を小さく抑える効果(情報のまとまりの捉えやすさ)が出てくると思います。


本件そのものについていえば質問者さんのコードだって充分分かり易いと思いますが、もう少し複雑なコードになっても耐えられるようなものを書く練習として、上記あるいはその他の設計・コーディングの仕方について経験を積むとよいと思います。言語機能(様々な構文, etc.)、基本的ライブラリーの機能(list, dict, etc.)などの知識をつけるのも大事ですが、結局は自分で色々書いてみるのが一番ではないでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/26 23:47

    ご回答ありがとうございました
    dictがまだ使い慣れていませんが、自分で色々書いて
    慣れていきたいと思います
    大変参考になりました

    キャンセル

+2

少し構造化してみました。while文のロジックを簡潔にするために全体としては長くなっています。

このコードの目標は次のようにwhileの中をシンプルにして、細かな判定やprint出力をBatterクラスに委譲することです。

N = int(input())

batter = Batter()

while True:
    a = input()
    try:
        if a == 'ball':
            batter.ball()
        if a == 'strike':
            batter.strike()
    except (BatterOut, BatterFourball):
        break

まず、三振アウトとフォアボール用の例外クラスを自作しておきます。この場合、例外クラスの名前だけが重要なので、中身はpassします。

class BatterOut(Exception):
    pass

class BatterFourball(Exception):
    pass

バッターはストライクかボールだけ実装します(残念ながらヒットを打てないバッターです)。

class Batter:
    def __init__(self):
        self._ball = 0
        self._strike = 0

    def strike(self):
        self._strike += 1
        print("strike!")
        if self._strike == 3:
            print("out!")
            raise BatterOut

    def ball(self):
        self._ball += 1
        print("ball!")
        if self._ball == 4:
            print("fourball!")
            raise BatterFourball

strikeとballの処理を各メソッドに委譲しているわけです。


以上をまとめると、全体のコードは次のようになります。

class BatterOut(Exception):
    pass

class BatterFourball(Exception):
    pass

class Batter:
    def __init__(self):
        self._ball = 0
        self._strike = 0

    def strike(self):
        self._strike += 1
        print("strike!")
        if self._strike == 3:
            print("out!")
            raise BatterOut

    def ball(self):
        self._ball += 1
        print("ball!")
        if self._ball == 4:
            print("fourball!")
            raise BatterFourball

N = int(input())

batter = Batter()

while True:
    a = input()
    try:
        if  a == 'ball':
            batter.ball()
        if  a == 'strike':
            batter.strike()
    except (BatterOut, BatterFourball):
        break

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/26 23:21

    回答ありがとうございます
    クラスを使ってシンプルにするという方法もありですね
    まだクラスについても作りなれていないので
    是非参考にさせていただきます

    キャンセル

+2

簡潔かは微妙ですけど、せっかく書いたので参考までに載せときます。

from enum import Enum, auto


class JudgeForPitch(Enum):
    STRIKE = auto()
    BALL   = auto()
    FOUL   = auto()

    @classmethod
    def get_input(cls, *, print_message):
        while True:
            print_message()
            try:
                return {
                    "strike": cls.STRIKE,
                    "ball"  : cls.BALL,
                    "foul"  : cls.FOUL,
                }[input()]
            except KeyError:
                pass


AtBatResult = Enum('AtBatResult', ['STRUCK_OUT', 'FOUR_BALL'])
def turn_at_bat():
    jfp = JudgeForPitch

    counts = {
        jfp.STRIKE: 0, jfp.BALL: 0
    }

    while True:
        thrown_result = jfp.get_input(
            print_message=lambda: print('input strike, ball or foul', end=': ')
        )
        if thrown_result in counts:
            counts[thrown_result] += 1
        elif thrown_result is jfp.FOUL:
            counts[jfp.STRIKE] += (counts[jfp.STRIKE] < 2)

        if counts[jfp.STRIKE] == 3:
            return AtBatResult.STRUCK_OUT
        if counts[jfp.BALL] == 4:
            return AtBatResult.FOUR_BALL

        print(
            'BALL: {0}, STRIKE: {1}'.format(
                counts[jfp.BALL], counts[jfp.STRIKE]
            )
        )


if __name__ == '__main__':
    print(turn_at_bat())

条件と異なりますが、一打席勝負にしています。ヒットや凡打は出ませんが。
いちおうファール判定、入力チェックも機能として組み込んでます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/26 23:40

    ご回答ありがとうございます
    大変参考になりました
    助かりました

    キャンセル

+2

3 項演算子と、groupby を使って書いてみました。

from itertools import groupby

MESSAGES = {
    "strike": "strike!", "ball": "ball!"
    "_fb": "foreball!", "_out": "out"
}
N = int(input())

records = []
for i in range(0, N):
    records.append(input())
    score = {k: len(list(g)) for k, g in groupby(sorted(records))}
    judge = "_fb" if score.get("ball", 0) == 4 else "_out" if score.get("strike", 0) == 3 else records[-1]
    print(MESSAGES.get(judge, "?"))
    if judge == "_fb" or judge == "_out":
        break

judge を求める部分は 3 項演算子でなく、メソッドとして独立させ そのなかでは case で書いたほうがよいとはおもいますが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/26 23:35

    ご回答ありがとうございます
    groupby をまだ使ったことがないので
    リファレンスを読みながらコードを
    ありがたく拝見させていただきます
    助かりました

    キャンセル

+2

質問のコードですが、それほど冗長ではありません。一度読めば理解できる、良いコードです。

複雑なテクニックを使うと、かえって可読性を損なう場合があります。それなら多少長くても、愚直に書いた方が良い場合も多いのです。

それはそれとして、小手先のテクニックで文字数を減らしてみました。でもほとんど変えていません。

N = int(input())
s = b = 0  # 一括代入できる

for _ in range(N):  # whileをforに変える。ループ変数は要らないので_にしておく(pythonの慣習)
    a = input()
    if  a == 'ball':
        b += 1  # 加算代入演算子を使う
        if  b < 4:
            print('ball!')
        else:
            print('fourball!')
            break
    if  a == 'strike':
        s += 1
        if  s < 3:
            print('strike!')
        else:
            print('out!')
            break

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/27 02:22

    ご回答ありがとうございます
    読みやすいコード助かります
    大変参考になりました

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Python 3.x

    7338questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。