第9章の解答。以下ネタバレなので、読みたくない方は立ち去りましょう。問題文は載せませんので悪しからず。必要なら買いましょう。その前にこの章の個人的なメモ。
- setオブジェクトで保持される順序にルールはない。
- 集合はハッシュテーブルというデータ構造に格納される。
- 集合の要素に対し、ハッシュコードが計算される。ハッシュコードはイミュータブル(書き換え不可能なデータ)のみ持つことが許される。
- 辞書の初期化は{}で行う。追加はappendなどではなく、辞書名[キー] = 値 という形式で指定すればOK。
- 185ページのdict_of_dicts.pyのサンプルコードに登場する辞書でJグドールに関する項目に'霊長類研究者'(英語では'primate researcher')とあるが、この後ろに「,」がないため、編集の必要がある。おそらくバグ。
1
# -*- encoding: sjis -*-
def find_dups(data):
h_table = {}
for d in data:
if d in h_table.keys(): # getメソッドを利用する方法もある
h_table[d] += 1
else:
h_table[d] = 1
ret = []
# iteritemsはオブジェクトのコピーを作らないので高速に動かすことができる
for (key, value) in h_table.iteritems():
if value >= 2:
ret.append(key)
return ret
if __name__ == '__main__':
data = range(10) + range(10, 5, -1) + range(3, 7)
ret = find_dups(data)
print ret
2
def mating_pairs(males, females):
''' the size of males and that of females are the same. '''
ret = []
# while males: is OK. this answer is a bit copmlex.
while len(males) > 0:
ret.append((males.pop(), females.pop()))
return set(ret)
if __name__ == '__main__':
males = set(['A', 'B', 'C', 'D', 'E'])
females = set(['1', '2', '3', '4', '5'])
print mating_pairs(males, females)
3
PDB形式のAUTHORの行に関する情報はこちらのpdfを参考にするとよいでしょう。人物名はコンマ区切り、AUTHORと人名は空白で区切られているという条件を使います。また、最後にコンマがあり人名が続かないこともありますので、そのようなことも考慮しています。
# -*- encoding: sjis -*-
import sys
def read_author(r):
ret = set()
for line in r:
if not line:
continue
if line[:6].upper() != 'AUTHOR': # AuthorなどもOKにするため、upper()を挟んでいる
continue
# 仕様の都合上8文字目から作者名が始まる可能性がある。
# strip()でいったん両端の空白を削除してから","で区切る
line = line[7:].strip().split(",")
print line
for i in range(len(line)):
if not line[i]: # AUTHORとあるが、何も続かない場合
continue
ret.add(line[i])
return ret # setを返しておく
if __name__ == '__main__':
names = set()
for filename in sys.argv[1:]: # 外部からファイルを読み込みたい
infile = open(filename, 'r')
name = read_author(infile)
names = names.union(name) # 和集合はunionである
infile.close()
print names
4(自信薄)
setの中に中身が変更可能なsetが入れられないのでfrozensetにして入れている。vowelは要素にfrozensetのデータを持つが、すでに通常のsetとして扱って問題ない。中身が書き換え可能なメモリアドレスにvowelsが配置されるということになる。
5
def count_values(dic):
values = {}
for (key, value) in dic.iteritems():
if not value in values:
values[value] = 1
return len(values)
if __name__ == '__main__':
print count_values({'red':1, 'green':2, 'blue':3})
print count_values({'red':1, 'green':2, 'blue':1})
print count_values({'red':2, 'green':2, 'blue':2})
6
def min_elementary_particle(dic):
minimum = 1.1 # probability is always smaller than 1
elem = ''
for (key, value) in dic.iteritems():
if value < minimum:
minimum = value
elem = key
return elem
if __name__ == '__main__':
component = {'neutrino':0.14 , 'neutron':0.55,
'proton':0.21, 'meson':0.03, 'muon':0.07}
print min_elementary_particle(component)
7
def count_duplicates(dic):
dup = {}
for (key, value) in dic.iteritems():
if not value in dup:
dup[value] = 1
else:
dup[value] += 1
dup_list = []
for (key, value) in dup.iteritems():
if value >= 2:
dup_list.append(key)
return len(dup_list)
if __name__ == '__main__':
color = {'red':1, 'green':4, 'blue':2, 'orange':3, 'green':2, 'orange':6}
print count_duplicates(color) # 2 is duplicated number.
8
例外という先の内容を無理やり利用しろということなので、試しに使ってみた。
def fetch_and_set(dic, key, new_value):
if key in dic:
old_value = dic[key]
dic[key] = new_value
return old_value
else:
raise KeyError("Unable to replace value for nonexistent key")
if __name__ == '__main__':
color = {'red':1, 'green':4, 'blue':2, 'orange':3}
print fetch_and_set(color, 'green', 100) # 4
try:
fetch_and_set(color, 'black', 3) # KeyError exception
except KeyError:
print "KeyError occurred!"
9
問題の仕様にはないのですが、辞書には必ずしもキーがR、G、Bだけではないものと仮定します。RGB以外は無視するものとします。
def is_balanced(dic):
total = 0
for (key, value) in dic.iteritems():
if key == 'R' or key == 'G' or key == 'B':
total += value
if abs(total - 1.0) < 10 ** -15: # almost equal to 1
return True
else:
return False
if __name__ == '__main__':
color = {'R':0.1, 'G':0.4, 'B':0.5}
print is_balanced(color) # True
color = {'R':0.1, 'G':0.4, 'X':0.2, 'B':0.5}
print is_balanced(color) # True
color = {'R':0.1, 'G':0.4, 'B':0.2}
print is_balanced(color) # False
10
両方の辞書に同じキーと同じ値が含まれている対のみ返します。同じキーだが値が違うという場合はdict_intersectの返り値の辞書には含まないものとします。
def dict_intersect(dic1, dic2):
k2 = dic2.keys()
ret = {}
for (k1, v1) in dic1.iteritems():
if k1 in k2 and v1 == dic2[k1]:
ret[k1] = v1
return ret
if __name__ == '__main__':
c1 = {'red':1, 'green':4, 'blue':2, 'orange':3, 'green':2, 'orange':6}
c2 = {'red':1, 'green':5, 'blue':2, 'yellow ':3}
print dict_intersect(c1, c2) # blue and red are extracted
11
scientists = {
'jgoodall' : {'surname' : 'Goodall',
'forename' : 'Jane',
'born' : 1934,
'died' : None,
'notes' : 'primate researcher', # distributed sample code lacks comma.
'author' : ['In the Shadow of Man', 'The Chimpanzees of Gombe']},
'rfranklin' : {'surname' : 'Franklin',
'forename' : 'Rosalind',
'born' : 1920,
'died' : 1957,
'notes' : 'contributed to discovery of DNA'},
'rcarson' : {'surname' : 'Carson',
'forename' : 'Rachel',
'born' : 1907,
'died' : 1964,
'notes' : 'raised awareness of effects of DDT',
'author' : ['Silent Spring']}
}
def db_headings(dic):
ret = []
for values in dic.values():
ret += values.keys()
return set(ret)
if __name__ == '__main__':
print db_headings(scientists)
12
scientistsは上のデータを利用することにします。rfranklinは他の二つとキーが異なるため内部の辞書は常に一定にはなりません。そのため、db_consistent関数にscientistsを渡すと、Falseになります。if elem.symmetric_difference(set(values)):は要素数が0でなければという意味になります。
def db_consistent(dic):
first = dic.keys()[0]
elem = set(dic[first].keys())
for values in dic.values():
if elem.symmetric_difference(set(values)):
return False
return True
if __name__ == '__main__':
print db_consistent(scientists)
13
空行で機能が大まかに区切られているので、それを1つのまとまりとしてモジュールにするというだけですね。
import sys
def create_dic(files):
count = {}
for filename in sys.argv[1:]:
infile = open(filename, 'r')
for line in infile:
name = line.strip()
count[name] = count.get(name, 0) + 1
infile.close()
return count
def reverse_dict(dic):
freq = {}
for (name, times) in dic.iteritems():
print name, times
if times in freq:
freq[times].append(name)
else:
freq[times] = [name]
return freq
def print_dic(dic):
for key in sorted(dic):
print key
for name in dic[key]:
print ' ', name
if __name__ == '__main__':
dic = create_dic(sys.argv[1:])
r_dic = reverse_dict(dic)
print_dic(r_dic)
14
a)、b)の解答は以下の通り。
def sparse_add(dic1, dic2):
ret = dic1.copy()
keys = set(dic1.keys() + dic2.keys())
for key in keys:
ret[key] = 0
if key in dic1.keys():
ret[key] += dic1[key]
if key in dic2.keys():
ret[key] += dic2[key]
return ret
def sparse_dot(dic1, dic2):
ret = 0
for (key, value) in dic1.iteritems():
if key in dic2:
ret += value * dic2[key]
return ret
if __name__ == '__main__':
v1 = {0:1, 6:3, 7:-1}
v2 = {1:1, 2:2, 6:1, 7:-1}
print sparse_add(v1, v2) # {0: 1, 1: 1, 2: 2, 6: 4, 7: -2}
print sparse_dot(v1, v2) # 4
c)に関しては、疎ベクトルの長さが、辞書の長さになるのか、それとも最後に現れた辞書のキーの値+1になるのかということが曖昧になっている。0を値に持つキーをどのように扱うかということが、「疎ベクトルの長さ」という言葉だけでははっきりしないということである。