プロが教える店舗&オフィスのセキュリティ対策術

Pythonのプログラミングの質問です。
今学校で習っているのですが、理解出来ないところがありましたので教えて頂けたらと思います。
関数を使って生年月日を分解して1の位まで足してでてくる数字を求めたいのですが、エラーがおこってしまいます。
私は最後の部分が違っていると思うのですが、どう直せばいいかわかりません。どなたか教えて頂けないでしょうか…。
以下がコードです。↓


print("あなたの生年月日の年を西暦で入力してください。")
y = input()
print("あなたの生年月日の月を入力してください。")
m = input()
print("あなたの生年月日の日を入力してください。")
d = input()
seinengappi = y + m + d

def kansu_suuzi(seinengappi):
while True:
a = 0
for i in seinengappi:
a += int(i)

if a < 10 or a==11 or a==22 or a==33 or a==44:
break
return a
seinengappi = str(a)

h = input()
kansu_suuzi(int(h))
print("1の位は…", h)

gooドクター

A 回答 (6件)

・・・・・・こういう事したいのかしらん。



# ここから
print("あなたの生年月日の年を西暦で入力してください。")
y = input()
print("あなたの生年月日の月を入力してください。")
m = input()
print("あなたの生年月日の日を入力してください。")
d = input()
seinengappi = y + m + d

def kansu_suuzi(seinengappi):
 a = sum([int(i) for i in list(seinengappi)])
 while a > 9:
  a = sum([int(i) for i in list(str(a))])
 return a

h = kansu_suuzi(seinengappi)
print("1の位は・・・", h)

# ここまで
    • good
    • 1

> 返り値と戻り値がまだ理解できていなく



返り値と戻り値は同じ意味です。
・・・うん、実はプログラミング初心者的には理解が難しい、ってのは不思議ではないんですよ。
初心者状態でその辺すんなり理解出来る人が半数くらい、そうじゃない人も半数くらい、ですかね(ちなみに僕も後者でした)。
そして「どのレベルで説明するのか」なかなか厄介なのも事実なんです。

例えば、ですね。「1足す2を計算しなさい」って問題があるとする。
バカみたいですが、これくらい簡単な問題を考えます。
往年のBASIC、ないしは古いスタイルのプログラミングとしてはこういう事をやったりしてました。

# 旧くてダメなスタイル
ans = 0

def hoge():
 global ans
 ans = 1 + 2

hoge()

print(ans)

## ここまで

ここで関数hogeは返り値を持ちません。そして関数hoge内部で大域変数ansを変更してる。
実は往年のプログラミングテクニックってこんなんばっかなんですよ。ぶっちゃけ、現在のC言語なんかでも似たような事をやらせてます(特に配列絡みで)。
一方、現代的なやり方は、多分貴方もコッチの方が馴染みがあるでしょうが、次のように書きます。

# 現代的なスタイル

def hoge():
 return 1 + 2

print(hoge())

## ここまで

旧くてダメなスタイルと現代的なスタイルを比較してみれば分かるでしょうが、

・ 旧くてダメなスタイルは大域変数を設定してそれを書き換える、と言うカンジで答えを求めてる。
・一方、現代的なスタイルでは返り値を用いてる。そしてその場合、実行されてる関数を別の関数(この場合はprint)の引数にしても構わない。返り値をアテにして変数に代入する事も出来る
・旧くてダメなスタイルと現代的なスタイルを比較するとコード量は現代的なスタイルの方が少ない

ってのが違いです。特に二番目が返り値の大きな特徴、ないしは利用の仕方、ですね。

コンピュータサイエンス的に言うと、実は旧くてダメなスタイルで書かれている関数hogeは関数ではありません。コンピュータサイエンス的には手続き、あるいはプロシージャと呼びます。プロシージャは返り値を持たず、主に大域変数あるいは与えられたデータ(引数)を強制的に書き換える目的で作られます(もっと言うと、こういうのを「副作用目的」と言います)。
反面、関数の特徴は返り値を持つ事。場合によってはデータの書き換えはあり得るんですが、それを行う目的だけで作られる事はないのです。あくまで「返り値を返す」のが関数設計の目的です。

・手続き(プロシージャ)・・・・・・返り値が無い
・関数・・・・・・返り値がある

実際の話、Pythonでは全部が関数で、コンピュータサイエンス的な意味での「手続き」は「Noneを返す関数」と呼ばれてます。呼んで字の如く、返り値は自動的にNoneになる。

# 旧くてダメなスタイルをちょっと改造してみる

ans = 0

def hoge():
 global ans
 ans = 1 + 2

val = hoge() # 手続きhogeを実行して変数valに代入すると・・・・・・?

print(val) # valの中身はNoneになってる

# ここまで

もうちょっと理解を深める為に「1足す2足す3は何になるか」考えてみましょう。
これもデモンストレーションの為の問題なんで、実際はこんな、いずれにしてもバカな書き方はしないんですが、あくまでデモンストレーションの為、旧くてダメなスタイルと現代的なスタイルを比較してみます。

# 旧くてダメなスタイル

ans = 0

def hoge():
 global ans
 ans += 1

def fuga():
 global ans
 ans += 2

def piyo():
 global ans
 ans += 3

hoge()
fuga()
piyo()

print(ans)

## ここまで

# 現代的なスタイル

def hoge():
 return 1

def fuga(x):
 return x + 2

def piyo(x):
 return x + 3

print(piyo(fuga(hoge())))

## ここまで

まぁ問題自体はバカバカしいんでどーでもいいんですが、現代的なスタイルの場合、もちろん各関数の実行結果をそれぞれ変数に代入可能なんですが(フツーはそういった書き方をするでしょう)、上のデモのように、引数に関数の実行結果を直接ツッコんじゃって連鎖させる事も可能です。
・・・まぁ、この辺、結果書いたコードの「可読性」とどう折り合いを付けるのか、ってのがあって、上の例だと「やり過ぎだ」って感じる人もいるでしょう。
ただ、関数の返り値って何?と言う疑問に直接答えるより「どう返り値を用いるか」の方が意味が明確に伝わる事もあって、そういう意味だと上の現代的なスタイルの例は分かりやすいかな、とは思います。

ちなみに、Pythonのprintも実は「Noneを返す関数」で、このようにしてprintの「返り値」を見る事が出来ます。

>>> x = print("hoge")
hoge
>>> print(x)
None

「"hoge"を印字する」と言う作用は実は返り値ではなくって、返り値自体はNoneだ、って事がこれで分かりますね。しかもxにprint("hoge")を代入してxを何度実行させても二度と"hoge"は印字されません。
つまり、print関数の主目的はコンピュータサイエンス的には「与えられた引数を印字する事」じゃないのです。不思議ですがそうなんです。そしてそれで分かるのは印字自体は「計算」ではない、と言う事実。こういう関数を「副作用目的の関数」等と呼びます。
コンピュータ上の入出力は理論的には実は不可思議な分野で、いずれにせよ、print等の入出力関数は、我々ユーザーは「主目的」には興味がない。「副作用目的」で使っているのです。
    • good
    • 1

うーん、まずインデントは全角スペースに置き換えて


投稿した方がいいですよ。
半角スペースはこのサイトの仕様でみんな消えてしまうので・・・

pythonはインデントが命の言語だから削られるとなんだか
よくわからない。
    • good
    • 1

そうね。


#3氏も言ってる通り、関数の引数、そして返り値の使い方が、どう見てもまだ分かってない気がする・・・・・・。

あとね、Pythonでそのままバッチ式の・・・と言うか、BASICみたいな書き方するのは端的に言うと「間違ってます」。まぁ、出来なくはないけどさ。
この辺、教えるセンセーにも拠るんだろうけど、バッチ式ガンガンやらせよう、初心者相手だから、ってのはPython使ったプログラミングの教育としては大間違いなんだよ。
たまに(いや、かなりの確率で?)Pythonで「C言語的な教育」をやろう、ってヤツいるんだけど、そもそも言語の設計方針に逆らった事やらせて上手く行く筈がないの。
言語設計に沿った教育が本当なら必要なのね。

あと、細かいトコ言うと、例えば

1. input関数でユーザーに入力促す際にprintを使うのは無駄。input関数は引数に「ユーザーへのメッセージ」を含む事が出来る。

例)
☓ ダメな書き方:
print("あなたの生年月日の年を西暦で入力してください。")
y = input()

○ 良い書き方:
y = input("あなたの生年月日の年を西暦で入力してください。\n")

input:
https://docs.python.org/ja/3/library/functions.h …

2. リストを使いこなす。Pythonに限らないけど、プログラミング言語ではリストや配列と言ったようなデータ構造をバンバン使う。特にこの問題のような「総和を求めよ」と言う場合、一々チマチマと変数を「足し合わせる」よりは、対象をリストとしてsum関数で和を取った方が早い。

例)
☓ ダメな書き方:
for i in seinengappi:
 a += int(i)

○ 良い書き方:

例えば

>>> sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
55

等。

sum:
https://docs.python.org/ja/3/library/functions.h …

3. リスト内包表記を使いこなす事。
これは初心者には「難しい」と思ってる人が多いけどさに非ず。なるたけ初心者状態でこれを覚えなさい。少なくともPython設計者達はユーザーにこれを使いまくって欲しいと思っている。
繰り返しでforとかwhileを使いそうな際、書く前に「リスト内包表記が使えないか?」毎回自問する事。80%くらいはリスト内包表記を使えば問題はスムーズに解決する。特に2.との絡みで。

リストの内包表記:
https://docs.python.org/ja/3/tutorial/datastruct …

4. Pythonでバッチ式、ないしはBASIC的なプログラムを書きたい場合、シェバングとif __name__ == '__main__':に頼る事。必要な関数を定義したらそれをシェバングとif __name__ == '__main__':で挟んでしまって、必要なバッチ処理はif __name__ == '__main__':以降に記述する。

Pythonプログラムの先頭行の #! シバン(Shebang)について:
https://gammasoft.jp/python/python-shebang/

Pythonの if __name__ == ‘__main__’ の使い方【初心者向け】:
https://techacademy.jp/magazine/19129

5. Pythonでの代入は複数の代入を一行で記述する事が可能。それをやった方がPython「らしい」。

☓ ダメな書き方:

y = input("あなたの生年月日の年を西暦で入力してください。\n")
m = input("あなたの生年月日の月を入力してください。\n")
d = input("あなたの生年月日の日を入力してください。\n")

○ 良い書き方

y, m, d = input("あなたの生年月日の年を西暦で入力してください。\n"), \
    input("あなたの生年月日の月を入力してください。\n"), \
    input("あなたの生年月日の日を入力してください。\n")

まぁ、さすがにこういうケースの場合、一行で書くと辛かったりするので、一行があまりに長すぎる場合は"\"を入れてシンタックス上問題が無い、と言う事をインタプリタに通達して改行を行う事が出来る(これは関数の引数が長すぎる、ないしは多すぎる場合にも良く使われる手である)。

と言うわけで取り敢えずこの5つの心構えを用いて#2で書いたコードをより「Pythonらしく」書き直したのが次のコードです。

#!/usr/bin/env python3

def kansu_suuzi(seinengappi):
 while len(seinengappi) > 1:
  seinengappi = str(sum([int(i) for i in seinengappi]))
 return seinengappi

if __name__ == '__main__':
 y, m, d = input("あなたの生年月日の年を西暦で入力してください。\n"), \
     input("あなたの生年月日の月を入力してください。\n"), \
     input("あなたの生年月日の日を入力してください。\n")
 print("1の位は・・・{}".format(kansu_suuzi(y + m + d)))
    • good
    • 2
この回答へのお礼

回答ありがとうございます。丁寧な説明本当に感謝しております。
返り値と戻り値がまだ理解できていなくて、どのサイトをみても理解できかったためここにのせました。
アドバイスを糧にやってみます。ありがとうございました。

お礼日時:2021/06/24 20:27

関数引数の使い方に混乱がみられるようなので、そこらへん勉強してみては。


値を引数で戻すのではなく、returnで戻した値を使うようしたほうが良いかと。

今回の例だと、seinengappiが「00010101」から「99991231」までだとしたら、以下で計算できるかも:
seinengappi = '20210623'
h = (sum( int(x) for x in seinengappi ) - 1) % 9 + 1
print( seinengappi, 'の1の位は', h )
※ 2+0+2+1+0+6+2+3=16, 1+6=7

########
print("あなたの生年月日の年を西暦で入力してください。")
y = input()
print("あなたの生年月日の月を入力してください。")
m = input()
print("あなたの生年月日の日を入力してください。")
d = input()
seinengappi = y + m + d

def kansu_suuzi(seinengappi):
△while True:
△△a = 0
△△for i in seinengappi:
△△△a += int(i)
△△if a < 10 or a==11 or a==22 or a==33 or a==44:
△△△break
△△△return a
△△seinengappi = str(a)

h = input()
kansu_suuzi(int(h))
print("1の位は…", h)
    • good
    • 1
この回答へのお礼

回答ありがとうございます。
そうです、返り値がまだ理解できていません。
コード参考にさせていただきます。
ありがとうございました

お礼日時:2021/06/24 20:32

うーん、インデントも無茶苦茶なんで何がやりたいんだかサッパリ分からんぞ・・・・・・。


例えば

return a

って書いてる以上、これは関数kansu_suuzi内にあるはずなんだけど、その後seinengappi = str(a)とか書かれてて、これは実行されないはずだし・・・。
そのあと

h= input()

になってて、そして

kansu_suuzi(int(h))

・・・。そうすれば最初に書かれてたy、m、dって言う変数も無意味になる。

全般的にホント、コード的には「何がやりたいんだかサッパリ分からん」コードになっています。
    • good
    • 1

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

gooドクター

このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング