FP:辞書は、複数あん入りのおまんじゅう
このワークシートは[url=https://www.geogebra.org/m/twxxx3yq]Math by Code[/url]の一部です。
<辞書は複数あん入りのおまんじゅう>[br][br]辞書というのは単語帳ではありません。[br]辞書を英和辞書や和英辞書のように、[br]キー(k)を日本語、値(v)を英語にした、ペアアイテム(items)で辞書イメージを伝える人がいます。[br]これは、Dictionaryクラスのせまい使い方です。[br][br]辞書は、あっち、こっちという別のものをたばねたものです。[br]辞書のキー(k)はただの目印程度の軽いものです。[br]値(v)は数値、文字列、関数名、。。。なんでもいいのです。オブジェクトです。[br][br]だから、辞書は[b]オブジェクトの固まり[/b]ですよね。[br]それを、[b]順番ではなく、キーで区別できるように[br][color=#0000ff][size=150][size=200]複数のおいしいあんを詰め込んだ[br]おまんじゅう[/size][/size][/color][/b]です。[br][br]
[b][size=150]<データリストをバケツリレーでフィルタリング>[br][/size][/b][br]辞書にフィルタ関数を連続で使うと、[br]複数条件のフィルタリングがカンタンにできます。[br]たとえば、アドレス情報から、大人だけ拾って、Emailアドレスを取り出し、[br]そのドメインをリスト化したいなら次のようにできます。[br]records = [[br] {"name": "Haskell Function", "email": "Haskell@example.com", "age": 25},[br] {"name": "Marry Jane", "email": "Marry@example.net", "age": 15},[br] {"name": "Johson Jonson", "email": "Jonson@gmail.com", "age": 10},[br] {"name": "Steve Apple", "email": "Steve@gmail.com", "age": 20},[br] # Additional records...[br]][br]def is_adult(user_record):[br] return user_record["age"] >=18[br]def email_address(user_record):[br] return user_record["email"][br]def email_domain(email):[br] return email.split('@')[1][br]adult_domains = map([br] email_domain,[br] map([br] email_address,[br] filter(is_adult, records)[br] )[br] )[br]print(list(adult_domains))[br][OUT][br][b]['example.com', 'gmail.com'][br][/b][br]
[b][size=150]<関数辞書を作ろう>[/size][/b][br][br]値はオブジェクトならなんでもいいです。[br]だから[b]関数を値にして、キーをつけた関数辞書[/b]というのがよくあるFPでの使い方になります。[br]たとえば、類似する関数を連続して使うときは、[b]1つの変数にくるみ、キーとデータだけ変えるだけ[/b]でいいですね[br]def process_text(text):[br] return text.lower()[br]def join(txt1, txt2):[br] return txt1 + "\n" + txt2[br]action_map = {[br] "proc": process_text,[br] "join": join[br]}[br][br]def execute_action(action, *args):[br] if action in action_map:[br] return action_map[action](*args)[br] else:[br] raise ValueError("Invalid action")[br][br]text1 = execute_action("proc", "Which do you like OOP or FP?")[br]text2 = execute_action("proc", "I like FP.")[br]text3 = execute_action("join", text1, text2)[br]text4 = execute_action("proc", "Because, FP is lite and safe.")[br]text5 = execute_action("join", text3, text4)[br]print(text5)[br][OUT][br][b]which do you like oop or fp?[br]i like fp.[br]because, fp is lite and safe.[br][/b][br][br][url=https://www.geogebra.org/m/twxxx3yq#material/awtf6qjz]ビルダパターンFP[/url]では、[br]複数の処理を[b]ラムダ関数にキーをつけて丸めた関数辞書[/b]を作りましたね。[br]text_builder_env = {[br] "add_title": lambda c, text: f"{c}【★ {text} ★】\n",[br] "add_paragraph": lambda c, text: f"{c} {text}\n",[br] "add_bullet_point": lambda c, item: f"{c} ・ {item}\n",[br] "add_formula": lambda c, expr: f"{c} [式: {expr}]\n"[br]}[br]こうすれば、辞書のキーは交通整理のキーでしかない。[br]キーはただの名前。[br]辞書の中身のオブジェクトが重要。[br][b][color=#0000ff][size=150]辞書形式で1つにくるむことで、1変数として、処理関数にまるっと渡せる[/size][/color][/b]。[br]キーに反応すべき関数だけ、中身を開ければよかったね。[br]
[b][size=150]<辞書の合併は|演算子で>[br][/size][/b][br]リストは+でたし算できました。[br]辞書は|でたし算できます。もしもキーが|の左右でかぶっているときは、右を優先します。[br]これは、左のデータが更新されるという意味ではありません。[br][b]|合併は、安心してイミュータブルな演算として使えます。[br][/b]たとえば、[br]>>> d = {'spam': 1, 'eggs': 2, 'cheese': 3}[br]>>> e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}[br]>>> d | e[br]{'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}[br]>>> e | d[br]{'cheese': 3, 'aardvark': 'Ethel', 'spam': 1, 'eggs': 2}[br][br]おまんじゅう辞書が2つあります[b]。まんd+まんeは、d|eとかき、e優先です。[br]ああ、dがつぶれる![/b][br]そんなことはりません。[b][color=#0000ff]もとのdのとなりにd|eの新品まんじゅうができてます[/color][/b]。[br][br]1つめのおまんじゅうに2つめのおまんじゅうをかぶせて合体してみよう。[br]# 左右でおまんじゅうを合体。焼き印(キー)が被ったら、右(あと)のあんこが勝つルール[br]d = {'つぶあん': 1, 'しろあん': 2, '栗あん': 3}[br]e = {'栗あん': '特製大粒', '抹茶あん': '宇治'}[br][br]res = d | e[br]print(f"d (元のおまんじゅう) (ID: {id(d)}): {d}")[br]print(f"res (新築おまんじゅう) (ID: {id(res)}): {res}")[br][OUT][br][b]d (元のおまんじゅう) (ID: 2502432854720): {'つぶあん': 1, 'しろあん': 2, '栗あん': 3}[br]res (新築おまんじゅう) (ID: 2502432861696): {'つぶあん': 1, 'しろあん': 2, '栗あん': '特製大粒', '抹茶あん': '宇治'}[/b]
[b][size=150]<辞書はアンパック演算子で無傷で更新できる>[br][/size][/b][br]PEP 448で、アンパック演算子*(辞書の場合は**)が導入されました。[br]変数前に演算子をつけると、そのコレクションは展開されますが、その使用する位置で挙動が変わります。[br][br]コレクションの要素をフラット化して要素を展開するのことになります。[br]>>> dict(**{'x': 1}, y=2, **{'z': 3})[br]{'x': 1, 'y': 2, 'z': 3}[br]アンパック演算子は、キーがかぶってないと、その順にただ展開されるだけです。[br]>>> {'x': 1, **{'y': 2}}[br]{'x': 1, 'y': 2}[br]しかし、[br][b][color=#0000ff]キーがかぶると、右(あと)が優先されるので、このロジック無傷の更新ができるわけです。[br][/color]>>> {'x': 1, **{'x': 2}}[br]{'x': 2}[br]>>> {**{'x': 2}, 'x': 1}[br]{'x': 1}[br][/b][br][url=https://www.geogebra.org/m/twxxx3yq#material/tkdcszna]メディエータFP[/url]では、[br]1つののキーが部品名component_name、その値が各部品の状態辞書。[br]そして、状態辞書にはvisible,valueの2つのキーと対応する値をつけ、初期化した状態をまるっと書きました。[br]initial_state = {[br] "A": {"visible": True, "value": None},[br] "B": {"visible": True, "value": None},[br] "C": {"visible": True, "value": None}[br]}[br]state_v0=initial_state[br]state_v1 = consult_mediator(state_v0, "A", "TOGGLE", "ON")としたときの親玉関数[br]def consult_mediator(state: dict, component_name: str, event_type: str, value=None) -> dict:[br] # ユーザーの入力を現在の状態に反映したベースとなる状態を作成[br][b] updated_state = {[br] **state,[br] component_name: {**state[component_name], "value": value}[br] }[br][/b] # マッチングのために条件をタプルにまとめる[br] match (component_name, event_type, value): [br].....[br]これが面白いですね。[br]**stateで、initial_stateがズラーっと展開されます。[br]変えたい部分を後ろにつけているので、そこで、キーがかぶっていたら、変わるということです。[br]まず、部品名が変わるかもしれません。次に部品の状態も展開しておき、変更のあるキーの値だけ変えられます。[br]
[b][size=150]<振り返り>[/size][/b][br]dic.update(up)は、[b]dic | up [/b]で安全に[b]複数更新[/b]できますね。[br]dic[k] = vとdel dic[v]の更新と削除はどうでしょう。[br][br]telenum = {'sasaki': '09000900990', 'yamamoto': '09011231234', 'suzuki':'09056569898' }[br][b][br]tel2 = {**telenum | {'sasaki':'08012345678'}} #1キー更新[br][br][color=#0000ff] keyをk、valueをvとすると、[br]1アイテムはk:vになり、[br]全アイテムは.items()になります。[br]その中からk,vのペアを取り出しながらk!=deleteTargetKeyとすると内包表記削除ができるね[br][/color][/b][b]tel3 = {k: v for k, v in telenum.items() if k!='sasaki'} #1キー削除[br][/b][br]print(f"telenum (過去):(ID: {id(telenum)}) {telenum}") [br]print(f"tel2 (更新):(ID: {id(tel2)}) {tel2}") [br]print(f"tel3 (削除):(ID: {id(tel3)}) {tel3}") [br][br][OUT][br][b]telenum (過去):(ID: 2502432542592) {'sasaki': '09000900990', 'yamamoto': '09011231234', 'suzuki': '09056569898'}[br]tel2 (更新):(ID: 2502432538688) {'sasaki': '08012345678', 'yamamoto': '09011231234', 'suzuki': '09056569898'}[br]tel3 (削除):(ID: 2502433152000) {'yamamoto': '09011231234', 'suzuki': '09056569898'}[br][/b][br][color=#9900ff][u][size=150]課題:ジオジェブラで辞書のようなものは作れますか?[br][/size][/u][/color][br]位置ではなくキーに対応する本当の辞書形式のデータ構造はありません。[br]でも、異なるタイプのデータタイプの対応表のイメージは作れます。[br]n=slider(1,3,1)[br]names={"sasaki", "suzuki",yamada"}[br]nums={1234,5678,9101}[br]funcs={x+1, x^2, log(x)}[br]f(x)=funcs(n,x)[br]text1=""+names(n)+":"+nums(n)+"y=" + f + ""[br]text2=TableText(names,nums,funcs}[br][br]番号がキーのおまんじゅうでした。