集合とは、ハッシュ可能なオブジェクトの_ミュータブル_で_順序を持たない_コレクションです。
集合の要素はそれぞれ異なっている必要があり、重複した要素は許可されません。
複数の異なるデータ型はもちろん、tupleのtuplesのような入れ子構造も、すべての要素が_ハッシュ化_できる限り保持できます。
集合には、イミュータブルなfrozensetsという種類もあります。
集合は、他のデータ構造や要素のまとまりから重複をすばやく取り除くために最もよく使われます。 順序付けや重複の追跡が不要な場合に、効率的な比較に使われることもあります。
他のコレクション型(辞書、配列、タプル)と同様に、setsは次のことをサポートします。
for item in <set>による繰り返しinとnot inによる要素の有無の確認、len()による長さの計算、copy()による浅いコピーsetsがサポートしないのは次のとおりです。
+による連結setで要素の有無を確認するときの計算量は(平均すると)一定です。一方、listやstringで確認する場合は、データの長さが増えるにつれて計算量も増えていきます。
<set>.union()、<set>.intersection()、<set>.difference()といったメソッドも、(平均すると)計算量は一定です。
setは、波括弧({})を使い、要素の間にカンマを置く_集合リテラル_として直接書くことができます。
重複は暗黙のうちに省略されます。
>>> one_element = {'➕'}
{'➕'}
>>> multiple_elements = {'➕', '🔻', '🔹', '🔆'}
{'➕', '🔻', '🔹', '🔆'}
>>> multiple_duplicates = {'Hello!', 'Hello!', 'Hello!',
'¡Hola!','Привіт!', 'こんにちは!',
'¡Hola!','Привіт!', 'こんにちは!'}
{'こんにちは!', '¡Hola!', 'Hello!', 'Привіт!'}
集合リテラルはdictリテラルと同じ波括弧を使うため、空のsetを作るにはset()を使う必要があります。
set()(setクラスのコンストラクター)は、引数として渡された任意のiterableに使うことができます。
iterableの要素は順に取り出され、1つずつsetに追加されます。
要素の順序は保持されず、重複は暗黙のうちに省略されます。
# To create an empty set, the constructor must be used.
>>> no_elements = set()
set()
# The tuple is unpacked & each element is added.
# Duplicates are removed.
>>> elements_from_tuple = set(("Parrot", "Bird",
334782, "Bird", "Parrot"))
{334782, 'Bird', 'Parrot'}
# The list is unpacked & each element is added.
# Duplicates are removed.
>>> elements_from_list = set([2, 3, 2, 3, 3, 3, 5,
7, 11, 7, 11, 13, 13])
{2, 3, 5, 7, 11, 13}
ここで説明する「アンパック」の挙動のため、文字列に対してset()を使うと意外な結果になるかもしれません。
# String elements (Unicode code points) are
# iterated through and added *individually*.
>>> elements_string = set("Timbuktu")
{'T', 'b', 'i', 'k', 'm', 't', 'u'}
# Unicode separators and positioning code points
# are also added *individually*.
>>> multiple_code_points_string = set('अभ्यास')
{'अ', 'भ', 'य', 'स', 'ा', '्'}
集合は異なるデータ型や_入れ子_のデータ型を保持できますが、setの要素はすべて_ハッシュ可能_でなければなりません。
# Attempting to use a list for a set member throws a TypeError
>>> lists_as_elements = {['🌈','💦'],
['☁️','⭐️','🌍'],
['⛵️', '🚲', '🚀']}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
# Standard sets are mutable, so they cannot be hashed.
>>> sets_as_elements = {{'🌈','💦'},
{'☁️','⭐️','🌍'},
{'⛵️', '🚲', '🚀'}}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
集合には、一般に数学的な集合演算を模したメソッドがあります。
これらのメソッドのほとんど(すべてではありません)には、対応する演算子があります。
メソッドは一般に任意のiterableを引数に取りますが、演算子は演算の両辺がsetsまたはfrozensetsである必要があります。
<set>.isdisjoint(<other_collection>)メソッドは、setsの要素が別のsetの要素と重なりを持つかどうかを調べるために使います。
このメソッドは、任意のiterableやsetを引数に取ります。
2つの集合に共通の要素がまったくない場合はTrueを、要素を共有している場合はFalseを返します。
これに対応する演算子はありません。
# Both mammals and additional_animals are lists.
>>> mammals = ['squirrel','dog','cat','cow', 'tiger', 'elephant']
>>> additional_animals = ['pangolin', 'panda', 'parrot',
'lemur', 'tiger', 'pangolin']
# Animals is a dict.
>>> animals = {'chicken': 'white',
'sparrow': 'grey',
'eagle': 'brown and white',
'albatross': 'grey and white',
'crow': 'black',
'elephant': 'grey',
'dog': 'rust',
'cow': 'black and white',
'tiger': 'orange and black',
'cat': 'grey',
'squirrel': 'black'}
# Birds is a set.
>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
# Mammals and birds don't share any elements.
>>> birds.isdisjoint(mammals)
True
# There are also no shared elements between
# additional_animals and birds.
>>> birds.isdisjoint(additional_animals)
True
# Animals and mammals have shared elements.
# **Note** The first object needs to be a set or converted to a set
# since .isdisjoint() is a set method.
>>> set(animals).isdisjoint(mammals)
False
<set>.issubset(<other_collection>)は、<set>のすべての要素が<other_collection>にも含まれているかどうかを調べるために使います。
演算子を使うと<set> <= <other_set>と書けます。
# Both mammals and additional_animals are lists.
>>> mammals = ['squirrel','dog','cat','cow', 'tiger', 'elephant']
>>> additional_animals = ['pangolin', 'panda', 'parrot',
'lemur', 'tiger', 'pangolin']
# Animals is a dict.
>>> animals = {'chicken': 'white',
'sparrow': 'grey',
'eagle': 'brown and white',
'albatross': 'grey and white',
'crow': 'black',
'elephant': 'grey',
'dog': 'rust',
'cow': 'black and white',
'tiger': 'orange and black',
'cat': 'grey',
'squirrel': 'black'}
# Birds is a set.
>>> birds = {'crow','sparrow','eagle','chicken', 'albatross'}
# Set methods will take any iterable as an argument.
# All members of birds are also members of animals.
>>> birds.issubset(animals)
True
# All members of mammals also appear in animals.
# **Note** The first object needs to be a set or converted to a set
# since .issubset() is a set method.
>>> set(mammals).issubset(animals)
True
# Both objects need to be sets to use a set operator
>>> birds <= set(mammals)
False
# A set is always a loose subset of itself.
>>> set(additional_animals) <= set(additional_animals)
True
<set>.issuperset(<other_collection>)は.issubset()の逆です。
<other_collection>のすべての要素が<set>にも含まれているかどうかを調べるために使います。
演算子を使うと<set> >= <other_set>と書けます。
# All members of mammals also appear in animals.
# **Note** The first object needs to be a set or converted to a set
# since .issuperset() is a set method.
>>> set(animals).issuperset(mammals)
True
# All members of animals do not show up as members of birds.
>>> birds.issuperset(animals)
False
# Both objects need to be sets to use a set operator
>>> birds >= set(mammals)
False
# A set is always a loose superset of itself.
>>> set(animals) >= set(animals)
True
<set>.intersection(*<other iterables>)は、元のsetとすべての<others>に共通する要素を持つ新しいsetを返します(言い換えると、すべてが交わるsetです)。
このメソッドの演算子版は<set> & <other set> & <other set 2> & ... <other set n>です。
>>> perennials = {'Annatto','Asafetida','Asparagus','Azalea',
'Winter Savory', 'Broccoli','Curry Leaf','Fennel',
'Kaffir Lime','Kale','Lavender','Mint','Oranges',
'Oregano', 'Tarragon', 'Wild Bergamot'}
>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Marjoram',
'Summer Squash', 'Okra','Shallots', 'Basil',
'Cilantro', 'Cumin', 'Sunflower', 'Chervil',
'Summer Savory'}
>>> herbs = ['Annatto','Asafetida','Basil','Chervil','Cilantro',
'Curry Leaf','Fennel','Kaffir Lime','Lavender',
'Marjoram','Mint','Oregano','Summer Savory',
'Tarragon','Wild Bergamot','Wild Celery',
'Winter Savory']
# Methods will take any iterable as an argument.
>>> perennial_herbs = perennials.intersection(herbs)
{'Annatto', 'Asafetida', 'Curry Leaf', 'Fennel', 'Kaffir Lime',
'Lavender', 'Mint', 'Oregano', 'Wild Bergamot','Winter Savory'}
# Operators require both groups be sets.
>>> annuals & set(herbs)
{'Basil', 'Chervil', 'Marjoram', 'Cilantro'}
<set>.union(*<other iterables>)は、<set>とすべての<other iterables>の要素を持つ新しいsetを返します。
このメソッドの演算子版は<set> | <other set 1> | <other set 2> | ... | <other set n>です。
>>> perennials = {'Asparagus', 'Broccoli', 'Sweet Potato', 'Kale'}
>>> annuals = {'Corn', 'Zucchini', 'Sweet Peas', 'Summer Squash'}
>>> more_perennials = ['Radicchio', 'Rhubarb',
'Spinach', 'Watercress']
# Methods will take any iterable as an argument.
>>> perennials.union(more_perennials)
{'Asparagus','Broccoli','Kale','Radicchio','Rhubarb',
'Spinach','Sweet Potato','Watercress'}
# Operators require sets.
>>> set(more_perennials) | perennials
{'Asparagus',
'Broccoli',
'Kale',
'Radicchio',
'Rhubarb',
'Spinach',
'Sweet Potato',
'Watercress'}
<set>.difference(*<other iterables>)は、元の<set>の要素のうち<others>に含まれない要素を持つ新しいsetを返します。
このメソッドの演算子版は<set> - <other set 1> - <other set 2> - ...<other set n>です。
>>> berries_and_veggies = {'Asparagus',
'Broccoli',
'Watercress',
'Goji Berries',
'Goose Berries',
'Ramps',
'Walking Onions',
'Blackberries',
'Strawberries',
'Rhubarb',
'Kale',
'Artichokes',
'Currants'}
>>> veggies = ('Asparagus', 'Broccoli', 'Watercress', 'Ramps',
'Walking Onions', 'Rhubarb', 'Kale', 'Artichokes')
# Methods will take any iterable as an argument.
>>> berries = berries_and_veggies.difference(veggies)
{'Blackberries','Currants','Goji Berries',
'Goose Berries', 'Strawberries'}
# Operators require sets.
>>> berries_and_veggies - berries
{'Artichokes','Asparagus','Broccoli','Kale',
'Ramps','Rhubarb','Walking Onions','Watercress'}
<set>.symmetric_difference(<other iterable>)は、<set>または<other>のいずれかにあるものの、両方にはない要素を含む新しいsetを返します。
このメソッドの演算子版は<set> ^ <other set>です。
>>> plants_1 = {'🌲','🍈','🌵', '🥑','🌴', '🥭'}
>>> plants_2 = ('🌸','🌴', '🌺', '🌲', '🌻', '🌵')
# Methods will take any iterable as an argument.
>>> fruit_and_flowers = plants_1.symmetric_difference(plants_2)
>>> fruit_and_flowers
{'🌸', '🌺', '🍈', '🥑', '🥭','🌻' }
# Operators require both groups be sets.
>>> fruit_and_flowers ^ plants_1
{'🌲', '🌸', '🌴', '🌵','🌺', '🌻'}
>>> fruit_and_flowers ^ set(plants_2)
{'🥭', '🌴', '🌵', '🍈', '🌲', '🥑'}
3つ以上の集合の対称差を取ると、結果のsetには、それぞれのsetに固有の要素だけでなく、その系列の中で3つ以上の集合の間に共有されている要素も含まれます(詳しくはsymmetric differenceに関するWikipediaの記事を参照してください)。
その系列の各setに固有の要素だけを得るには、すべての2集合の組み合わせの積集合を別の手順で集約してから、取り除く必要があります。
>>> one = {'black pepper','breadcrumbs','celeriac','chickpea flour',
'flour','lemon','parsley','salt','soy sauce',
'sunflower oil','water'}
>>> two = {'black pepper','cornstarch','garlic','ginger',
'lemon juice','lemon zest','salt','soy sauce','sugar',
'tofu','vegetable oil','vegetable stock','water'}
>>> three = {'black pepper','garlic','lemon juice','mixed herbs',
'nutritional yeast', 'olive oil','salt','silken tofu',
'smoked tofu','soy sauce','spaghetti','turmeric'}
>>> four = {'barley malt','bell pepper','cashews','flour',
'fresh basil','garlic','garlic powder', 'honey',
'mushrooms','nutritional yeast','olive oil','oregano',
'red onion', 'red pepper flakes','rosemary','salt',
'sugar','tomatoes','water','yeast'}
>>> intersections = (one & two | one & three | one & four |
two & three | two & four | three & four)
...
{'black pepper','flour','garlic','lemon juice','nutritional yeast',
'olive oil','salt','soy sauce', 'sugar','water'}
# The ^ operation will include some of the items in intersections,
# which means it is not a "clean" symmetric difference - there
# are overlapping members.
>>> (one ^ two ^ three ^ four) & intersections
{'black pepper', 'garlic', 'soy sauce', 'water'}
# Overlapping members need to be removed in a separate step
# when there are more than two sets that need symmetric difference.
>>> (one ^ two ^ three ^ four) - intersections
...
{'barley malt','bell pepper','breadcrumbs', 'cashews','celeriac',
'chickpea flour','cornstarch','fresh basil', 'garlic powder',
'ginger','honey','lemon','lemon zest','mixed herbs','mushrooms',
'oregano','parsley','red onion','red pepper flakes','rosemary',
'silken tofu','smoked tofu','spaghetti','sunflower oil', 'tofu',
'tomatoes','turmeric','vegetable oil','vegetable stock','yeast'}
ビジネスパートナーと一緒に、小さなケータリング会社を営んでいます。地元の料理クラブのために、催しを開くことになりました。そのクラブでは、クラブで人気の料理が振る舞われます。クラブは大きな催しを開くことに慣れておらず、準備や買い出し、下ごしらえ、盛り付けまで、助けが必要です。そこで、計画のすべてを効率よく進めるために、小さなPythonスクリプトをいくつか書くことにしました。
催しのレシピはあちこちから集めたもので、材料が重複した(それ以上のこともある)登録があるようです。余分な材料を買い込むのは避けたいところです。 買い出しと調理を始める前に、各料理の材料リストを「整理」する必要があります。
料理名と材料のlistを受け取り、最初の要素に料理名、そのあとに重複を取り除いた材料のsetを続けたtupleを返すclean_ingredients(<dish_name>, <dish_ingredients>)関数を実装してください。
>>> clean_ingredients('Punjabi-Style Chole', ['onions', 'tomatoes', 'ginger paste', 'garlic paste', 'ginger paste', 'vegetable oil', 'bay leaves', 'cloves', 'cardamom', 'cilantro', 'peppercorns', 'cumin powder', 'chickpeas', 'coriander powder', 'red chili powder', 'ground turmeric', 'garam masala', 'chickpeas', 'ginger', 'cilantro'])
>>> ('Punjabi-Style Chole', {'garam masala', 'bay leaves', 'ground turmeric', 'ginger', 'garlic paste', 'peppercorns', 'ginger paste', 'red chili powder', 'cardamom', 'chickpeas', 'cumin powder', 'vegetable oil', 'tomatoes', 'coriander powder', 'onions', 'cilantro', 'cloves'})
催しでは、カクテルと「モクテル」の両方が提供されます。モクテルは、アルコール_なし_のミックスドリンクです。 モクテルが本当にノンアルコールであること、そしてカクテルには本当にアルコールが_含まれている_ことを確認する必要があります。
飲み物の名前と材料のlistを受け取るcheck_drinks(<drink_name>, <drink_ingredients>)関数を実装してください。
アルコールを含む材料がない場合は飲み物の名前に"Mocktail"を続けたものを、アルコールが含まれる場合は飲み物の名前に"Cocktail"を続けたものを返してください。
この演習では、カクテルに含まれるアルコールはsets_categories_data.pyのALCOHOLS定数のものだけとします。
>>> from sets_categories_data import ALCOHOLS
>>> check_drinks('Honeydew Cucumber', ['honeydew', 'coconut water', 'mint leaves', 'lime juice', 'salt', 'english cucumber'])
...
'Honeydew Cucumber Mocktail'
>>> check_drinks('Shirley Tonic', ['cinnamon stick', 'scotch', 'whole cloves', 'ginger', 'pomegranate juice', 'sugar', 'club soda'])
...
'Shirley Tonic Cocktail'
ゲストには食事制限のある人もいるため、スタッフは料理をVegan、Vegetarian、Paleo、Keto、Omnivoreに分ける必要があります。 料理は、その材料がすべてカテゴリーの材料セットに含まれている場合にのみ、そのカテゴリーに属します。
料理名とその料理の材料のsetを受け取るcategorize_dish(<dish_name>, <dish_ingredients>)関数を実装してください。
関数はdish name: <CATEGORY>の形の文字列を返してください(_その料理がどの食事カテゴリーに属するか_を示します)。
与えられる料理はすべて、sets_categories_data.pyから読み込むカテゴリー(VEGAN、VEGETARIAN、PALEO、KETO、OMNIVORE)のいずれかに「当てはまります」。
>>> from sets_categories_data import VEGAN, VEGETARIAN, PALEO, KETO, OMNIVORE
>>> categorize_dish('Sticky Lemon Tofu', {'tofu', 'soy sauce', 'salt', 'black pepper', 'cornstarch', 'vegetable oil', 'garlic', 'ginger', 'water', 'vegetable stock', 'lemon juice', 'lemon zest', 'sugar'})
...
'Sticky Lemon Tofu: VEGAN'
>>> categorize_dish('Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole', {'shrimp', 'bacon', 'avocado', 'chickpeas', 'fresh tortillas', 'sea salt', 'guajillo chile', 'slivered almonds', 'olive oil', 'butter', 'black pepper', 'garlic', 'onion'})
...
'Shrimp Bacon and Crispy Chickpea Tacos with Salsa de Guacamole: OMNIVORE'
ゲストの中には、アレルギーやその他の食事制限がある人もいます。 問題が起きないよう、こうした材料には料理ごとにタグや注記を付ける必要があります。
最初の要素に料理名、2番目の要素にその料理の材料のlistまたはsetを持つtupleを受け取るtag_special_ingredients(<dish>)関数を実装してください。
料理名と、料理の説明に特別な注記が必要な材料のsetを返してください。
listの中の材料には、重複がある場合もあれば、ない場合もあります。
この演習では、ラベルを付ける必要のあるアレルゲンや特別な材料はすべて、sets_categories_data.pyから読み込むSPECIAL_INGREDIENTS定数に含まれています。
>>> from sets_categories_data import SPECIAL_INGREDIENTS
>>> tag_special_ingredients(('Ginger Glazed Tofu Cutlets', ['tofu', 'soy sauce', 'ginger', 'corn starch', 'garlic', 'brown sugar', 'sesame seeds', 'lemon juice']))
...
('Ginger Glazed Tofu Cutlets', {'garlic','soy sauce','tofu'})
>>> tag_special_ingredients(('Arugula and Roasted Pork Salad', ['pork tenderloin', 'arugula', 'pears', 'blue cheese', 'pine nuts', 'balsamic vinegar', 'onions', 'black pepper']))
...
('Arugula and Roasted Pork Salad', {'pork tenderloin', 'blue cheese', 'pine nuts', 'onions'})
発注と買い出しの準備として、メニューにあるすべての料理について、材料の「マスターリスト」を作成する必要があります(分量はあとで記入します)。
料理のlistを受け取り、挙げられたすべての料理のすべての材料のsetを返すcompile_ingredients(<dishes>)関数を実装してください。
個々の料理は、その材料のsetで表されます。
dishes = [ {'tofu', 'soy sauce', 'ginger', 'corn starch', 'garlic', 'brown sugar', 'sesame seeds', 'lemon juice'},
{'pork tenderloin', 'arugula', 'pears', 'blue cheese', 'pine nuts',
'balsamic vinegar', 'onions', 'black pepper'},
{'honeydew', 'coconut water', 'mint leaves', 'lime juice', 'salt', 'english cucumber'}]
>>> compile_ingredients(dishes)
...
{'arugula', 'brown sugar', 'honeydew', 'coconut water', 'english cucumber', 'balsamic vinegar', 'mint leaves', 'pears', 'pork tenderloin', 'ginger', 'blue cheese', 'soy sauce', 'sesame seeds', 'black pepper', 'garlic', 'lime juice', 'corn starch', 'pine nuts', 'lemon juice', 'onions', 'salt', 'tofu'}
主催者からは、トレーで出す「一口サイズ」の前菜として用意してほしい料理のリストが渡されています。 これらは、大きめの取り分けとして用意する料理のメインリストから抜き出す必要があります。
料理名のlistと前菜名のlistを受け取るseparate_appetizers(<dishes>, <appetizers>)関数を実装してください。
関数は、前菜名を取り除いた料理名のlistを返してください。
<dishes>と<appetizers>のどちらのlistにも重複が含まれる可能性があり、重複を取り除く必要があるかもしれません。
dishes = ['Avocado Deviled Eggs','Flank Steak with Chimichurri and Asparagus', 'Kingfish Lettuce Cups',
'Grilled Flank Steak with Caesar Salad','Vegetarian Khoresh Bademjan','Avocado Deviled Eggs',
'Barley Risotto','Kingfish Lettuce Cups']
appetizers = ['Kingfish Lettuce Cups','Avocado Deviled Eggs','Satay Steak Skewers',
'Dahi Puri with Black Chickpeas','Avocado Deviled Eggs','Asparagus Puffs',
'Asparagus Puffs']
>>> separate_appetizers(dishes, appetizers)
...
['Vegetarian Khoresh Bademjan', 'Barley Risotto', 'Flank Steak with Chimichurri and Asparagus',
'Grilled Flank Steak with Caesar Salad']
各カテゴリー(Vegan、Vegetarian、Paleo、Keto、Omnivore)の中で、1つの料理にしか登場しない材料を抜き出します。 こうした「シングルトン」の材料には専任の買い出し担当を割り当て、ほかの作業に追われて忘れてしまわないようにします。
料理のlistと、同じカテゴリーの<CATEGORY>_INTERSECTIONS定数を受け取るsingleton_ingredients(<dishes>, <INTERSECTIONS>)関数を実装してください。
各料理は、その材料のsetで表されます。
各<CATEGORY>_INTERSECTIONSは、そのカテゴリーで2つ以上の料理に登場する材料のsetです。
集合演算を使って、関数は「シングルトン」の材料(カテゴリー内で1つの料理にしか登場しない材料)のsetを返してください。
from sets_categories_data import example_dishes, EXAMPLE_INTERSECTION
>>> singleton_ingredients(example_dishes, EXAMPLE_INTERSECTION)
...
{'garlic powder', 'sunflower oil', 'mixed herbs', 'cornstarch', 'celeriac', 'honey', 'mushrooms', 'bell pepper', 'rosemary', 'parsley', 'lemon', 'yeast', 'vegetable oil', 'vegetable stock', 'silken tofu', 'tofu', 'cashews', 'lemon zest', 'smoked tofu', 'spaghetti', 'ginger', 'breadcrumbs', 'tomatoes', 'barley malt', 'red pepper flakes', 'oregano', 'red onion', 'fresh basil'}