練習問題

ランダムなリスト

0 から 100 までの整数がランダムに格納されたリストを返す関数 randlist を作成してください。引数でリストのサイズを指定します。randlist の実行例を以下に示します。

>>> randlist(10)
[31, 98, 49, 2, 90, 27, 12, 0, 11, 54]
>>> randlist(5)
[85, 29, 92, 40, 18]
>>> randlist(3)
[43, 2, 89]

解答

ランダムなリスト(範囲指定)

「ランダムなリスト」で作成した randlist で、リストに格納される整数の範囲をキーワード引数から指定できるようにして下さい。

randlist の実行例を以下に示します。

>>> randlist(10)                           #  0 から 100 まで
[31, 52, 81, 9, 85, 11, 13, 82, 25, 51]
>>> randlist(5, lower=20)                  # 20 から 100 まで
[95, 20, 31, 51, 83]
>>> randlist(3, upper=50)                  #  0 から  50 まで
[12, 31, 23]
>>> randlist(6, lower=20, upper=50)        # 20 から  50 まで
[24, 38, 38, 40, 44, 47]

解答

比較関数を関数に渡してソート

以下の関数 qsort は、昇順(小さい値から順に並べる)でソートを行っています。

def qsort(lst):
  if lst == []:
    return lst
  else:
    x, xs = lst[0], lst[1:]
    return qsort([a for a in xs if a < x]) + [x] + qsort([a for a in xs if a >= x])

もし降順(大きい値から順に並べる)でソートしたい場合にはどうしたらよいでしょうか。

などの方法もありますが、もっとうまい方法はないのでしょうか。問題を考え易くするために、qsort を少し修正してみます。

def qsort(lst):
  if lst == []:
    return lst
  else:
    def partition(x, lst): # x を基準に lst を「チビ」と「デカ」に分割する
      a, b = [], []
      for i in lst:
        if i < x:       # *1*
	  a.append(i)   # 「チビ」
	else:
	  b.append(i)   # 「デカ」
      return a, b
      
    xs, ys = parition(lst[0], lst[1:])
    return qsort(xs) + [lst[0]] + qsort(ys)

*1* に注目してください。

if i < x:

の所を

if i > x:

に書き換えると、降順でソートされることになります。つまり値を比較する部分だけを書き換えるだけで昇順、降順のどちらにもソートできるようになります。

この比較を行う部分を qsort が受け取った関数が行うように qsort を書き直してください。つまり、比較を行う関数(比較関数)を qsort が受け取るようにします。

実行例を以下に示します。

>> qsort(lambda x, y: x < y, [2, 4, -90, 3, 10])
[-90, 2, 3, 4, 10]
>> qsort(lambda x, y: x > y, [2, 4, -90, 3, 10])
[10, 4, 3, 2, -90]

また、文字列をその長さの順にソートする比較関数 slencmp を作成してください。

実行例を以下に示します。

>>> qsort(slencmp, ["short", "double", "int", "long"])
['int', 'long', 'short', 'double']

解答

2つのファイルを並列に表示

2 つのファイルを並列に表示するプログラムを作成してください。

例えば add10.py と bytecode.txt というファイルがあるとします:

add10.py:

def add10(n):
  return n + 10

bytecode.txt:

 0 LOAD_FAST                0 (n)
 3 LOAD_CONST               1 (10)
 6 BINARY_ADD          
 7 RETURN_VALUE        
 8 LOAD_CONST               0 (None)
11 RETURN_VALUE        

add10.py と bytecode.txt ファイルを次のように並列に表示します(境界には " | " を入れます)。

> python par.py add10.py bytecode.txt
def add10(n):   |  0 LOAD_FAST                0 (n)
  return n + 10 |  3 LOAD_CONST               1 (10)
                |  6 BINARY_ADD
                |  7 RETURN_VALUE
                |  8 LOAD_CONST               0 (None)
                | 11 RETURN_VALUE

> python par.py bytecode.txt add10.py
 0 LOAD_FAST                0 (n)    | def add10(n):
 3 LOAD_CONST               1 (10)   |   return n + 10
 6 BINARY_ADD                        |
 7 RETURN_VALUE                      |
 8 LOAD_CONST               0 (None) |
11 RETURN_VALUE                      |

par.py を作成してください。なおタブ文字は考えません。

解答

ファイルの拡張子変更

ファイルの拡張子を変更するスクリプトを作成してください。といっても、ここでは本当にファイルの拡張子を変更するのではなく、変更メッセージを表示するだけにします。

対象ファイルはカレントディレクトリのファイルとし、拡張子はコマンドラインから指定します。実行例を以下に示します。

$ ls
sugar.txt salt.txt pepper.txt prog.py foo.py rename.py main.c

$ python rename.py .txt .qq
sugar.txt => sugar.qq
salt.txt => salt.qq
pepper.txt => pepper.qq

$ ls
sugar.txt salt.txt pepper.txt prog.py foo.py rename.py main.c 

$ python rename.py .py .l
prog.py => prog.l
foo.py => foo.l
rename.py => rename.l

実行例のように、変更されるファイルに対して

ファイル名 => 変更後のファイル名

と出力する rename.py を作成してください。

解答

mapconcat

mapconcat(function, sequence, separator) 

mapconcat 関数は 3 つの引数をとります。この関数は、sequence の各要素に function を適用して、その結果を繋げます。そのとき各要素のセパレータは separator となります。

function は 1 つの引数を受け取り、文字列を返す関数です。

実行例を以下に示します。

>>> mapconcat(str, ["foo", "bar", "baz"], "-")
'foo-bar-baz'
>>> mapconcat(str, [1, 2, 3], " ")
'1 2 3'
>>> mapconcat(lambda c: c*3, "abc", "")
'aaabbbccc'
>>> mapconcat(lambda s: s.rjust(10), ["foo", "bar", "baz"], "")
'       foo       bar       baz'

mapconcat 関数を作成して下さい。

解答

filterの自作

問題1:filter関数はシーケンスの中から特定の条件にあうものだけを取り出す関数です。たとえば整数のリストから偶数のみを取り出すには次のようにします。

>>> def iseven(n):
...   return n % 2 == 0
...
>>> filter(iseven, range(10))
[0, 2, 4, 6, 8]

filterと同じ動作をする関数myfilterを作成してください。ただしfilterと全く同じにする必要はありません(たとえばmyfilterに関数としてNoneを渡すことなどは考えなくて良い)。myfilterは関数とリストを受け取り、リストを返します。

myfilterの実行例:

>>> myfilter(iseven, range(10))
[0, 2, 4, 8]
>>> myfilter(lambda x: x < 5, range(-10, 10, 2))
[-10, -8, -6, -4, -2, 0, 2, 4]

問題2:引数として与えられたリストを条件に合うものと合わないものとに分ける関数partitionを作成してください。partitionはそれぞれのリストを返します。

sepの実行例:

>>> partition(iseven, range(10))
([0, 2, 4, 6, 8], [1, 3, 5, 7, 9])
>>> partition(lambda x: x < 10, [1, 5, -3, 120, 99, 7, 89])
([1, 5, -3, 7], [120, 99, 89])

問題3:次のようなタプルのリストがあります。それぞれのタプルの要素は生徒の名前とテストの点数となっています。

[("Beth", 43), ("Kathy", 80), ("Mark", 56), ("Mary", 70), ("Susie", 68)]

問題2で作成したpartition関数を使って、テストの点数が60点以上の人とそうでない人とに分けてください。

実行例:

>>> ls = [("Beth", 43), ("Kathy", 80), ("Mark", 56), ("Mary", 70), ("Susie", 68)]
>>> partition(ここにプログラムをかく, ls)
([("Kathy", 80), ("Mary", 70), ("Susie", 68)], [("Beth", 43), ("Mark", 56)])

解答

入れ子のリストを平らにする

入れ子になったリストを平らにして返す関数flattenを作成してください。

>>> flatten([1, 2, [3, 4]])
[1, 2, 3, 4]
>>> flatten([1, [2, 3], [4, [5]]])
[1, 2, 3, 4, 5]

解答

rangeの返す数列を生み出すジェネレータ

range関数は数列のリストを生成する関数です。range関数が返す数列と同じ値を生み出すジェネレータgrangeを作成してください。実行例は次のようになります。

>>> list(grange(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(grange(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(grange(0, 20, 2))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> list(grange(0, -10, -1))
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
>>> list(grange(0))
[]
>>> list(grange(1, 0))
[]
>>> for i in grange(5):
...   print i,
...
0 1 2 3 4

grangeは引数として整数を受け取ると仮定します。

解答


Python ページ