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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

4回答

1216閲覧

Pythonでeval,execを使った条件分岐課題の解法について

Basc

総合スコア1

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

3グッド

4クリップ

投稿2023/04/19 02:49

1.実現したいこと

 井田昌之先生(著)の「PythonとGoogleColaboratoryではじめる自然言語処理」内の
課題に対する解法について知りたい。

2.書籍内の取り組み課題

 書籍内のトピックで複雑な分岐処理が必要な時にeval()関数、exec()関数を用いる事例紹介がありました。
そのプログラム課題として以下のような性格診断テストの事例があり、

「必ずしもeval()などを使う必要はないが、単にif文で処理しようとすると、スマートには行かなくなる。あなたならどうするか?実際に作ってみて考察してみよう。」

 といった条件が付与されています。
イメージ説明

 以下のようなコードでとりあえず動いているのですが、どういう記述方法がスマートであるかが不明であり、
eval()やexec()を使った記述方法をご教示いただければ幸いです。
なお、本書籍は基本的に解答コードがないです。

3.自身の作成したコード

python

1question1 = "実際の年齢よりも、年上にみられることが多い[yes/no]" 2question2 = "声がでかい[yes/no]" 3question3 = "手先が器用で、結構まめだ。簡単な料理は作れる。[yes/no]" 4result1 = "好きなことには、ご飯を忘れても熱中する方" 5result2 = "君はクラス一のひょうきん君" 6result3 = "人前で注目を浴びるのが好き" 7 8def que1(): 9 print(question1) 10 res = input() 11 if res=="yes": 12 que2() 13 if res=="no": 14 que3() 15 16def que2(): 17 print(question2) 18 res = input() 19 if res=="yes": 20 rslt3() 21 if res=="no": 22 que3() 23 24def que3(): 25 print(question3) 26 res = input() 27 if res=="yes": 28 rslt1() 29 if res=="no": 30 rslt2() 31 32def rslt1(): 33 return print(result1) 34 35def rslt2(): 36 return print(result2) 37 38def rslt3(): 39 return print(result3) 40 41que1()
dameo, TakaiY, quickquip👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TakaiY

2023/04/19 06:34

> 複雑な分岐処理が必要な時にeval()関数、exec()関数を用いる事例 この本を持っていなくて、上記の内容がわからないので使って作る方針がよくわかりません。どんな感じなのかでもいいので、紹介されている方法書けませんか?
jbpb0

2023/04/19 07:43

書籍の「単にif文で処理しようとすると、スマートには行かなくなる。」は、pythonには「goto文」が無いので見難くなることがある、という説明の流れで書かれてるので、「単にif文で処理」とは、質問のコードのように関数を使うのではなく、質問のコードの関数定義を、関数が呼び出されて実行されるifブロックのところに全部展開して書くようなコードのことを指してる、と私は想像しました (個人の感想です)
TakaiY

2023/04/19 08:57

jbpb0さん、 情報ありがとうございます。 これは難解ですね。 どうするのがいいんだろう。 僕も回答が知りたい。
Basc

2023/04/19 23:43

TakaiYさん、おっしゃる通りで非常に気になる課題になっており、皆さんのお知恵をお借りできればと感じております。
can110

2023/04/20 04:47

書籍持ってないので文脈など不明ですが、主題が「自然言語処理」であることから マルコフ連鎖や正規表現などにも関連する「状態遷移」を使うとうまくいく課題と思います。
guest

回答4

0

ベストアンサー

「必ずしもeval()などを使う必要はないが、単にif文で処理しようとすると、スマートには行かなくなる。あなたならどうするか?

evalを使う必要ないのであれば

  • ツリーの各要素(メッセージ)に番号を振る。nodes
  • 要素のうち、質問(=枝分かれがある)ならyes,noの移動先を割り振る。questions

というデータを作ります。
あとは 最初の質問から答えに到達するまでループすればよいです。

Python

1from io import StringIO 2 3# 元データ:質問, yes, no の行先 4lines = """年上にみられる,声がでかい,手先が器用 5声がでかい,注目,手先が器用 6手先が器用,熱中,ひょうきん""" 7 8nodes = dict() 9questions = dict() 10 11# 元データ文字列から状態遷移データを作成 12items = {} 13no = 1 14for line in StringIO(lines): 15 items = line.strip().split(',') 16 for i,item in enumerate(items): 17 if item not in messages: 18 messages[items[i]] = no 19 nodes[no] = item 20 no += 1 21 items[i] = messages[item] 22 questions[items[0]] = (items[1],items[2]) 23 24print(nodes) 25print(questions) 26 27# 質問による状態遷移 28 29no = 1 # 最初の質問 30while True: 31 mes = nodes[no] 32 if no in questions: # 質問なので尋ねる 33 inp = input(f'あなたは{mes}?') 34 no = questions[no][inp!='yes'] 35 else: # 答えなので終了 36 print(f'あなたは{mes}!') 37 break

よく考えたら各要素(メッセージ)に番号を振る必要もないか。

Python

1from io import StringIO 2 3# 元データ:質問, yes, no の行先 4lines = """年上にみられる,声がでかい,手先が器用 5声がでかい,注目,手先が器用 6手先が器用,熱中,ひょうきん""" 7 8# 元データの文字列から状態遷移データを作成 9questions = dict() 10first = None 11for line in StringIO(lines): 12 q,y,n = line.strip().split(',') 13 if first is None: 14 first = q 15 questions[q] = (y,n) 16 17# 質問による状態遷移 18 19mes = first # 最初の質問 20while mes in questions: 21 inp = input(f'あなたは{mes}?') 22 mes = questions[mes][inp!='yes'] 23 24print(f'あなたは{mes}!')

この課題は、状態遷移(有限オートマトン)でシンプルに表現できて解けると思います。

Python

1from graphviz import Digraph 2 3G = Digraph(format="png") 4G.attr("node", fontname='MS Gothic') 5G.edge('年上にみられる', '声がでかい',label='yes') 6G.edge('年上にみられる', '手先が器用',label='no') 7G.edge('声がでかい', '注目',label='yes') 8G.edge('声がでかい', '手先が器用',label='no') 9G.edge('手先が器用', '熱中',label='yes') 10G.edge('手先が器用', 'ひょうきん',label='no') 11G.render("ret")

イメージ説明

投稿2023/04/19 06:05

編集2023/04/20 06:36
can110

総合スコア38266

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Basc

2023/04/20 00:15

can110さん、回答ありがとうございます。 no = questions[no][inp!='yes']のBool型の0,1で配列のインデックスをあてがう方法もあるんですね。 melianさんの方でも同じ使われ方をしていて、勉強になりました。
guest

0

すみません。回答ではありません。書籍の情報から、何が求められているのか考えてみました。

質問の元の書籍の該当箇所をみると、複数の条件で分岐させたいとききれいに表現できない、ということのようです。 条件が複雑になると、同じ処理を複数の場所に書かなければならなくなったりします。
関数化したりすることで記述を簡略化できるということもありますが、ロジックを分散させずに条件判定を1つで完結させたいということだと思います。

質問にある条件を以下のように書くと、2つのルートがある「手先が器用で...」の質問からの流れを2箇所に書くことになってしまいます。

python

1 2ans = input("実際の年齢よりも、年上にみられることが多い[yes/no]") 3 4if ans == 'yes': 5 ans = input("声がでかい[yes/no]") 6 if ans == "yes": 7 print("人前で注目を浴びるのが好き") 8 else: 9 ans = input("手先が器用で、結構まめだ。簡単な料理は作れる。") 10 if ans == "yes": 11 print("好きなことには、ご飯を忘れても熱中する方") 12 else: 13 print("君はクラス一のひょうきん君") 14else: 15 ans = input("手先が器用で、結構まめだ。簡単な料理は作れる。[yes/no]") 16 if ans == "yes": 17 print("好きなことには、ご飯を忘れても熱中する方") 18 else: 19 print("君はクラス一のひょうきん君") 20

もし、pythonにgotoがあって、以下のように書くことができれば、重複は避けられます。

※ 以下のコードはpythonではありません。

python

1ans = input("実際の年齢よりも、年上にみられることが多い[yes/no]") 2 3if ans == 'yes': 4 ans = input("声がでかい[yes/no]") 5 if ans == "yes": 6 print("人前で注目を浴びるのが好き") 7 else: 8 goto(LABELX) 9else: 10#!! LABELX 11 ans = input("手先が器用で、結構まめだ。簡単な料理は作れる。[yes/no]") 12 if ans == "yes": 13 print("好きなことには、ご飯を忘れても熱中する方") 14 else: 15 print("君はクラス一のひょうきん君")

さて、これを eval()/exec()を使えばうまく処理できるってことなのだと思いますが、どうすればいいんだろう。

投稿2023/04/19 11:09

TakaiY

総合スコア12765

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Basc

2023/04/19 23:53 編集

マークダウン記法慣れていないのでうまくいけばいいのですが。 いただいたプログラムのGoToのところを以下のような感じにしてみても 動いています。どうでしょうか? ``` def LABELX(): ans = input("手先が器用で、結構まめだ。簡単な料理は作れる。[yes/no]") if ans == "yes": print("好きなことには、ご飯を忘れても熱中する方") else: print("君はクラス一のひょうきん君") ans = input("実際の年齢よりも、年上にみられることが多い[yes/no]") if ans == 'yes': ans = input("声がでかい[yes/no]") if ans == "yes": print("人前で注目を浴びるのが好き") else: eval('LABELX()') ```
TakaiY

2023/04/20 01:00

その書き方だと、evalを使わずとも、単に関数を必要な場所で呼べばいいだけなので、違うと思います。
Basc

2023/04/20 01:11

そうですよね・・・。なのでこの書籍であえて使うべきでないといわれているevalをわざわざ紹介しており、どういう使い方を想定しているのかが気になります。
guest

0

eval()やexec()を使った記述方法をご教示いただければ幸いです。

eval()exec() は使っていないので、ご要望を満たすものではありませんが、分岐(branch)構造を nested tuple で表現して利用する方法を参考までに回答します。

python

1dext = ('手先が器用で、結構まめだ。簡単な料理は作れる。', 2 ('好きなことには、ご飯を忘れても熱中する方', '君はクラス一のひょうきん君')) 3loud = ('声がでかい', ('人前で注目を浴びるのが好き', dext)) 4test = ('実際の年齢よりも、年上にみられることが多い', (loud, dext)) 5 6if __name__ == '__main__': 7 p = test # shallow copy 8 while isinstance(p, tuple): 9 ans = input(f'{p[0]} [yes/no]') 10 p = p[1][ans != 'yes'] 11 print(p)

追記

eval() を使う場合は以下の様な感じに。

python

1def yes_or_no(prompt): 2 ans = input(f'{prompt} [yes/no]').strip().lower() 3 return 'yes' if ans in ('yes', 'y') else 'no' 4 5def eval_qa(reply, yn): 6 ans = eval(yes_or_no(reply), yn) 7 return ans() if callable(ans) else ans 8 9if __name__ == '__main__': 10 test = lambda: eval_qa('実際の年齢よりも、年上にみられることが多い', 11 {'yes': loud, 'no': dext}) 12 loud = lambda: eval_qa('声がでかい', 13 {'yes': '人前で注目を浴びるのが好き', 'no': dext}) 14 dext = lambda: eval_qa('手先が器用で、結構まめだ。簡単な料理は作れる。', 15 {'yes': '好きなことには、ご飯を忘れても熱中する方', 16 'no': '君はクラス一のひょうきん君'}) 17 18 print(test())

投稿2023/04/19 05:57

編集2023/04/23 01:56
melian

総合スコア19798

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Basc

2023/04/20 00:08

melianさん、回答ありがとうございます。 非常にシンプルなプログラムでおどろきました。とはいえ、私はBingのChatGPTさんに聞かないと中身の動きが理解できないくらいでした・・・。 配列とタプルの違いを勉強するきっかけになりました。 また、eval()を使ったプログラムはむしろ複雑になった感がありますね。
guest

0

質問勘違いのため削除

投稿2023/04/20 05:41

編集2023/04/21 05:40
bsdfan

総合スコア4567

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問