プログラミング入門I
2024.01.15
文字列1

Back to index page



  1. 本日の作業内容

  2. 前回の確認テストについて

    やはり確認テストではエラーが発生するプログラムが出てきますね.以前に言ってあるのですが,たとえ書きかけでも,コメントアウトの機能 # を使ってエラーが出ないようにしてあれば,残りの部分で採点しますので,とにかくエラーを回避するスキルについても身に着けておいてください.実際に自分がプログラミングする際にも役立ちます.

    実行時エラー: b2316 b2328 b2353

    番号出力間違い: b2317 b2327 b2252 prog4

    解答用紙の番号と名前が違う: b2317 b2321 prog4

    以下は例によって問題のあるプログラムの例です.参考にしてください.

    for i in range(1,21):
        print(f'{i:2} ',end='')
    else:
        print()
    for j in range(5):
        num=random.randint(1,20)
        print(f'{num:2}:',end='')
        for k in range(1,num+1):
            print('  *',end='')
        else:
            print()
    

    前回と同様なんですが,for 文のカウンタ用の変数の名前を j に変える必要は無いです.

    for i in range(1,21):
        print(f'{i:3}', end = '')
    else:
        print()
        num = random.randint(1,20)
        for j in range(1,6):
            for j in range(1,num):
            
                print('  *', end = '')
        else:
            print()
    

    どんな数が乱数で出てきたのかわからないので動作確認のしようもないのですが,内側の for 文が終わった段階での改行が無いので,全部1行に * を出力しています.(下の図)外側の for 文の else に処理を入れなくてもいいのに,といった前回のコメントがまだ反映されなかったようです.

          1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
      *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
    

    for i in range(1, 21):
        print(f'{i:3}', end = '')
    else:
        print()
    
    for _ in range(5):
        num1 = random.randint(1, 20)
        print(f'{num1:2}:', end = '')
        for j in range(1, i + 1):
            print('  *', end = '')
        else:
            print()
    

    無関係な最初の for 文で使われた変数 i がなぜか亡霊のように2つめの内側の for 文に登場しました.最初の for 文で変数 i の値は20になっているので,下のように全部の行で20個 * が出力されています.

    1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
    20:  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
    11:  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
     5:  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
    12:  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
     9:  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
    

        for _ in range (5):
        num = random.randint(1, 20)
        print(f'{num:2}:',end='')
        
        for _ in range (1, num):
            print(' * ',end='')
    
        else:
            print()
    

    range 関数の陥りやすい罠ですね.範囲を num までとすると,num - 1 までとなりますので,下のように * が一個足りません.

        1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
     1:
     8: *  *  *  *  *  *  * 
    12: *  *  *  *  *  *  *  *  *  *  * 
     6: *  *  *  *  * 
    13: *  *  *  *  *  *  *  *  *  *  *  * 
    

    for _ in range(5):
        num=random.randint(1,20)
        print(f'{num:2}:' ,end='')
        for i in range(1,21):
            if i <= num:
                print('  *' ,end='')
            else:
                print('   ' ,end='')
        else:
            print()
    

    if は要らないと言ったっしょ?言ったっしょ!

    for i in range(1,21):
        print(f'{i:3}: ', end='')
    

    print 関数の引数の中に : コロンがあるので,当然下のように表示されますよね.

      1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  6 * * * * * *
    

    for _ in range(5):
        num1 = random.randint(1,20)
        print(f'{num1:2}:', end = '')
    
        for j in range(1,21):
            if num1 <= j:
                print(' *', end = '')
            else:
                print('   ', end = '')
    
        print()
    

    * の表示幅が間違っているのでわかりづらいんですが,変数 num がカウンタ用の変数 j よりも小さかったら * を出力するということで,最初はスペースを表示し,超えたところで * を表示し始める,という間違った処理になっています.if 文を使うから下のようなことに.

       1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
     9:                         * * * * * * * * * * * *
    11:                               * * * * * * * * * *
     4:          * * * * * * * * * * * * * * * * *
     3:       * * * * * * * * * * * * * * * * * *
     3:       * * * * * * * * * * * * * * * * * *
    

    for _ in range(5):
        num1 = random.randint(1, 20)
        print(f'{num1:2}:', end = '')
        for i in range (1, num1 + 1):
            if num1 == i:
                print('  *', end = '')
            else:
                print('  *', end = '')
        else:
            print()
    

    if 文の式が真でも偽でも,文が同じなら意味ないじゃん.

    for _ in range(5):
        a = random.randint(1, 20)
        print(f'{a:2}:', end = '')
        print()
        for j in range(1, a):
            print(' *', end = '')
    

    そんなところで改行するから,下のようなことになる.

        1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
    10:
     * * * * * * * * *17:
     * * * * * * * * * * * * * * * *16:
     * * * * * * * * * * * * * * *11:
     * * * * * * * * * *15:
     * * * * * * * * * * * * * *
    

  3. 前回の宿題について

    今回はなかなか悲惨な状況でした.課題はビット演算だったのですが,ビット演算を使用していない処理がほとんどでした.それでは点数を上げることができません.残念です.

    実行時のエラーと相変わらず名前や番号に関する問題も出ていました.

    実行時エラー: b2325

    解答用紙の番号間違い: b2337

    以下は例によって問題のあるプログラムの例です.参考にしてください.

    for i in range(a, 0, -1):
        b = random.randint(0, 1)
        print(f'{b}',end = '')
        c = i - 1
        d = 2 ** c
        e = b * d
        f = f + e
    

    こういう風に意味のない変数名を付けるのをやめましょうということを以前から言っているのですが,まだ使う人がいましたね.しかも,無駄に変数をたくさん用意してメモリの無駄遣いだし,わかりにくくなるし.

    num1 = random.randint(5, 8)
    maxnum = 0
    base = 0
    
    for i in range(0, num1):
        maxnum += 2 ** i
        num2 = random.randint(0, 1)
        base += num2 * maxnum
    
    one = maxnum - base
    two = one + 1
    
    print(f'Number of digits in binary and max in decimal: {num1}, {maxnum}')
    print(f'Base number:      {base:0{num1}b} -- {base:>3}')
    print(f"One's complement: {one:0{num1}b} -- {one:>3}")
    print(f"Two's complement: {two:0{num1}b} -- {two:>3}")
    

    さて,今回大量にできてきたのが上のパターンです.for 文は必要無いですし,さらに補数について,ビット演算ではなくただの四則演算で用意しています.題意に合わないので,大幅減点となりました.

    num1 = random.randint(5,8)
    print('Number of digits in binary and max in decimal: 7, 127')
    num2 = random.randint(7,127)
    m = 2 ** num1 - 1
    a=int(f'{num2:0{num1}b}',2)
    b=a+1
    print(f'Base number:      {num2:0{num1}b}--{num2}')
    print(f'One\'s complement:{~a & m: 0{num1+1}b}--{a}')
    print(f'Two\'s complement:{~b & m: 0{num1+1}b}--{b}')
    

    発生させる2進数の桁数に関係なくベースの数を作っているので,下のようにおかしいことになります.せっかくビット演算をしているのにね.

    Number of digits in binary and max in decimal: 7, 127
    Base number:      111010--58
    One's complement: 000101--58
    Two's complement: 000100--59
    

    n = random.randint(5,8)
    if n == 5:
        num = random.randint(16,31)
    elif n == 6:
        num = random.randint(32,63)
    elif n == 7:
        num = random.randint(64,127)
    else:
        num = random.randint(128,255)
    
    onescom = 2**n - num-2    
    twoscom = 2**n - num-1
    
    m = 2**n - 1
    
    print(f'Number of digits in binary and max in decimal: {n}, {num}')
    print(f"One's complement: {~num % m:0{n}b} -- {onescom}")
    print(f"Two's complement: {~num % m + 1:0{n}b} -- {twoscom}")
    

    上のような if による鬼の場合分けをする習慣は早くなくしましょう.また,後半はよくわからなくなって来ていますし.

    num1=random.randint(5,8)
    print(f'Number of digits in binary and max in decimal:{num1},127')
    

    こちらもその桁の最大数がなぜか127固定だし.

    num1 = random.randint(5, 8)
    num2 = random.randint(0, 255)
    

    桁数に関係なくベースとなる数を発生させています.

    print(f'Number of digits in binary and max in decimal: {num1}, {2 ** num1 - 1}')
    print(f"Base number:      {format(num2, '0' + str(num1) + 'b' )} -- {num2}")
    print(f"Ones complement:  {format(num3, '0' + str(num1) + 'b' )} -- {num3}")
    print(f"Twos complement:  {format(num4, '0' + str(num1) + 'b' )} -- {num4}")
    

    なぜ文字列をこんな風に作らないといけないのかがわかりません.

    bnum=random.randint(1,num2)
    onenum=bnum+1
    
    print(f'Base number:      {bnum:0{num1}b} -- {bnum:3}')
    print(f'One\'s complement: {~onenum & num2:0{num1}b} -- {~onenum & num2:3}')
    print(f'Two\'s complement: {~bnum & num2:0{num1}b} -- {~bnum & num2:3}')
    

    せっかくビット演算をしようとしているのに,最初によくわからない足し算をしているので,違うものになってしまっています.1の位が同じになっちゃいますね.

    Number of digits in binary and max in decimal: 7, 127
    Base number:      1101100 -- 108
    One's complement: 0010010 --  18
    Two's complement: 0010011 --  19
    

    num1 =random.randint(5,8)
    num2 =random.randint(0,2**num1-1)
    
    hosuu = num2 & ((1 << num1)-1)
    hosuu2 = (num2^((1 << num1)-1))+1
    
    print("Number of digits in binary and max in decimal:", num1, ",", 2**num1 - 1)
    print("Base number:      {:0{width}b} --  {}".format(num2, num2, width=num1))
    print("One's complement: {:0{width}b} --  {}".format(hosuu, hosuu, width=num1))
    print("Two's complement: {:0{width}b} --  {}".format(hosuu2, hosuu2, width=num1))
    

    なぜシフト演算子?結果は下のようになって,意味不明.

    Number of digits in binary and max in decimal: 8 , 255
    Base number:      11100010 --  226
    One's complement: 11100010 --  226
    Two's complement: 00011110 --  30
    

    def generate_binary(length):
        return format(random.getrandbits(length), f'0{length}b')
    
    def ones_complement(binary_str):
        return ''.join('1' if bit == '0' else '0' for bit in binary_str)
    
    def twos_complement(binary_str):
        return format(int(binary_str, 2) + 1, f'0{len(binary_str)}b')
    
    def binary_to_decimal(binary_str):
        return int(binary_str, 2)
    

    こんな面倒なことは求めていないのですがね.それでも処理があっているのならまだしも,間違っているし.

  4. 前回の復習

    ビット演算子の活用について学習しました.ただ,中身はビットの並びを意識してよく考えないとわからないややこしいものでしたし,複合演算子なども出てきましたので,余計に難しくなったかもしれません.いずれどこかで出てきたら,またその時に改めて考えましょう.

  5. 文字列

    1. 文字列の基礎
    2. 変数名などで文字の並んだものをこれまで使用してきましたが,値として文字の並びを持つものが文字列です.文字が1文字であっても Python は文字列として扱うことになっています.

      インデックス

      文字列のそれぞれの文字には順番に応じて番号が振られており,それをインデックスと言います.先頭を 0 番としそれ以降 1, 2, 3, … と続きます.また,末尾の文字から順番に数えていきたいときには -1, -2, -3 … のように負の数で表現します.このインデックスは反復処理などで文字列を扱う際に重要です.

      s = 'python'
      
      print(s[1])
      

      上のようにすると,文字列 'python' の2文字目の y が出力されます.このようにインデックスは [ ] で指定します.

      len 関数

      len(str) は文字列 str の文字数を返す関数ですが,この例で str は文字列オブジェクトの変数名であり,値の方の文字数を返すことはわかるとおもいます.

      スライス

      スライスは Python に特徴的な操作で文字列の中から任意の文字を取り出すことができます.教科書 p.139 (旧版では p.137) にスライスで文字を抽出する際の例がありますので,ぜひ見ておいてください.

    3. 文字列の操作
    4. 文字列に対してさまざまな操作を行うメソッドが用意されています.次回以降演習問題を通して学習していきましょう.

    5. 書式化
    6. ここまで f 文字列を多く利用してきましたが,他にも % 記法や format メソッドがあります.こちらは必要に応じて使用しましょう.

  6. 演習

    今回の演習問題です.

  7. 本日のまとめ

    文字列に関するいくつかの操作を学習しました.次回以降も使用する処理としてはランダムな文字からなる文字列の生成です.以下のように,類纂代入演算子 += と反復処理を利用して任意の長さの文字を作ることができます.

    s = ''
    
    for _ in range(5):
        s += chr(random.randint(97, 122))
    

    上の例はアルファベットの小文字をランダムに5個並べた文字列の生成の例です.

  8. 宿題

    宿題が公開されるのは明日火曜日10:00の予定で,締切りは22日の10:00です.レポート提出システムを使用します.

  9. 次回の予習範囲

    次回も引き続き教科書のp.134-162(旧版は p.132-159)の範囲を学習しますので,予習をしてきてください.

    なお,次回は最後ですので,授業の終わりに確認テストを実施します.


目次ページに戻る