【供養】echoコマンドっぽいのが動くpythonプログラム

pythonでほんのすこしだけshellのような挙動をするものを作れという課題が出たからやってたけど、勝手にos.system()使っちゃダメだと判断してたせいでいらないものを作ってしまった。そこそこ時間がかかったのに消すのも癪に障るので一応とっておこう(echoをpythonで実装する課題とかなさそうだけど)
引数のargsはechoの後ろにある引数をリストにまとめたものです。 echo -e "a"と入力した時はargsの中身は[['-e', '"a"']]になっている。
echoだけだと[False]

def echo(args):
    e, n = False, False #e: エスケープ文字を解釈, n: 最後に改行しない
    literal = []

    for args2 in args:
        if args2 == False:
            return
        for arg in args2:
            match arg:
                case '-e':
                    e = True
                case '-n':
                    n = True
                case '-en':
                    e, n = True, True
                case '-ne':
                    e, n = True, True
                case _:
                    if arg[0] == arg[-1] == '"':
                        literal.append(arg[1:-1])
                    else:
                        print("Invalid argument ->", arg)
                        return
    print_str(literal, e, n)

def print_str(line, e, n):
    if not e and not n:
        print(str(line).replace("[", "").replace("]", "").replace(",", "").replace("'", "").replace("\\\\", "\\"))
    elif e and not n:
        result: str = str(line).replace("[", "").replace("]", "").replace(",", "").replace("'", "").replace("\\\\", "\\").replace('\\n', '\n').replace('\\t', '\t')
        print(result)
    elif not e and n:
        print(str(line).replace("[", "").replace("]", "").replace(",", "").replace("'", "").replace("\\\\", "\\"), end="")
    else:
        result: str = str(line).replace("[", "").replace("]", "").replace(",", "").replace("'", "").replace("\\\\", "\\").replace('\\n', '\n').replace('\\t', '\t')
        print(result, end="")

print_strの条件分岐絶対もっときれいに書く方法ある…

英検2級→TOEIC400

23年の冬に英検2級を受けて、その後すぐにTOEICも受けた。2級が受かってたので500~600くらいかなと思ってたらまさかの400。
TOEICに割ける時間もそんなにないのにこのスコアはちょっとまずいな…

https://www.mext.go.jp/b_menu/shingi/chousa/shotou/117/shiryo/__icsFiles/afieldfile/2016/05/24/1368985_15_1.pdf

を見ると2級が550~になっている。550寄越せや!!!

ちなみに私の学校では英検2級もしくはTOEIC540~750(うろ覚え)で2単位もらえる。英検2級なら中学時代英語の偏差値が40くらいで安定してた私でも試験前日の午後に出る順パス単のAとBを詰め込んだら合格できたので、てっとり早く単位がほしいのであればおすすめ。問題も簡単だし。あとCBTとホンモノの試験両方受けたけどCBTの方が若干易しい?(CBTでも普通の試験と同じように単位認定してくれる)



ということでTOEICも単語を詰め込めばどうにかなると踏んでいて。 ZENPENとかで他の編入試験受けた方がよく使ってた銀のフレーズと金のフレーズを購入したのでひとまずTOEIC対策はこれ一本でやっていきます。一日単語50個+定形表現5個でちょうど今月末終わりそう。
ZENPENの体験談を見ると600点台くらい欲しい感じですね。

Cの練習2

パスカルの三角形

forの二重ループの内側の条件分岐はswitchで記述したかったけど、caseの後ろに続く値は定数しか使えないのでif文で代用した。 多重配列は要素数が異なる配列を宣言するのが一応できるけど難しそうなのでやめた。for文で配列の要素を取り出すのもインデックスの値を定義してやらないといけない。

#include <stdio.h>
#include <string.h>

void line(int);

int main() {
    int row;//この行まで表示
    scanf("%d", &row);
    if (row <= 0) {main();}
    line(row);
    return 0;
}

void line(int row) {
    int triangle[row][row];
    memset(triangle, 0, sizeof(triangle)); //二重配列を0で初期化
    for (int i = 0; !(i+1 > row); i++) { //i:三角形の縦方向
        for (int j = 0; !(j+1 > row); j++) { //j:三角形の横方向
            if (j == 0 || j == i) { //左端か右端
                triangle[i][j] = 1;
            }
            else if (j > i) { //三角形の外側(いらない部分)
                break;
            }
            else {
                triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j];
            }
        }
    }
    for (int r = 0; !(r >= row); r++) {
        for (int l = 0; l <= r; l++) {
            printf("%d, ", triangle[r][l]);
        }
        printf("\n");
    }
}
バブルソート

Cのポインタはある値が格納されているメモリ上のアドレスを指し示すもの、ということはわかったがコンパイラに怒られながらじゃないと作れない。 Rustの所有権の概念とかのほうがよっぽどすんなりと受け入れられた。

#include <stdio.h>

int *bubble_sort(int *, int);
int swap(int *, int, int);


int main() {
    int ls[] = {4, 6, 2, 3, 5, 1, 3};
    int length = sizeof(ls)/sizeof(int); //配列の長さ
    int *result = bubble_sort(ls, length); //配列の長さは配列のポインタから取り出せない
    for (int i = 0; i != length; i++) {
        printf("%d", result[i]);
    }
    return 0;
}

int *bubble_sort(int *ls, int length) {
    while (length != 1) {
        for (int i = 0; i != length; i++) {
            if (ls[i] > ls[i+1] && i+1 != length) {
                *ls = swap(ls, i, i+1);
            }
        }
        length--;
    }
    return ls;
}

int swap(int *arr, int left, int right) {
    //left, rightはインデックスの数値;
    int tmp = arr[left];
    arr[left] = arr[right];
    arr[right] = tmp;
    return *arr;
}

Cの練習1

いくつか練習になりそうなテーマを考えたのでコーディングしてみる。

フィボナッチ数列

再帰を理解していればわりと簡単。実際にプログラムをループさせたいときってだいたいforかwhileでなかなか再帰を使う機会がないのに、参考書ではやけに目にする。

#include <stdio.h>

int fib(int);

int main() {
    int n;
    scanf("%d", &n); //フィボナッチ数列のn番目の値を求める
    if (n <= 0) {
        printf("invalid value\n");
        main();
    }
    int result = fib(n);
    printf("%d", result);
    return 0;
}

int fib(int x) {
    int tmp;
    switch(x) {
        case 1:
            tmp = 1;
            break;
        case 2:
            tmp = 1;
            break;
        default:
            tmp = fib(x-2) + fib(x-1);
    }
    return tmp;
}
素因数分解
  • for文・while

こっちはなかなか大変だった。慣れてないのもあるかもしれないが、リストを使わないで実装するとなると難しい。(隙あらばリストを使ってたので)
変数とかifの条件分岐はもう少し減らせそうだ。
詰まったらネットで検索しつつ調べていくと、べき乗の演算子が存在しないらしい。今回は自分で実装したけど、n乗の計算をしたい時はライブラリの関数を使うそうな。

C言語/演算子と式 - Wikibooks

を見ると「**」にはまだ何も割り当てられていないのに何の理由で実装されなかったんだろうか。

#include <stdio.h>

int try_div(int, int);
int my_pow(int, int);

int main() {
    //このプログラムは不完全で、素数mを入力すると「m = 」としか出力されないし
    //最後に余計な乗算記号がついてしまう
    int n;
    scanf("%d", &n);
    if (n < 1) {
        printf("invalid value\n");
        main();
    }
    printf("%d = ", n);
    int num; //素数の底
    for (num = 2; num != n; num++){
        if (n % num == 0) {
            int pw = try_div(n, num);
            if (pw != 0) {printf("%d^%d * ", num, pw);}
        }
    }
    return 0;
}

int try_div(int n, int num) {
    int tmp = 2;
    int sup = 1; //素数の冪数
    //最初にnumが素数か確かめる
    while (tmp < num) {
        if (num%tmp == 0) {
            return 0;//numが素数ではなかった
        }
        tmp++;
    }
    //この行に到達しているならnumは素数
    for (; n%my_pow(num, sup) == 0; sup++) {}
    return sup - 1;
}

int my_pow(int base, int exponent) {
    //mathライブラリのpowと違ってfloatを引数に取れない・exponent < 1だと動かない
    int x = 1;
    for (; exponent > 0; exponent--) {
        x *= base;
    }
    return x;
}

次はパスカルの三角形とバブルソートとかにしよう。 パスカルの三角形なんかは2次元配列を使うので慣れるにはいいかもしれない。

編入試験対策のためにC言語に触れてみる

編入試験でC?

工学部情報系の3年次編入での専門科目の問題はほぼほぼC言語を使う問題なので、ソートアルゴリズムの復習も兼ねてC言語に触れて読み書きできるようにせねば…という次第

(https://www.adm.kanazawa-u.ac.jp/south/s_gakusei/pdf/r6/r6_a_subutsu.pdf みたいにFortranが使えるような大学もごく一部あるが)

それにしてもPythonとかJavaを使っている問題が全く見つからなかったんだけど、Cが採用されるのはやっぱり低級言語寄りな言語だからなのだろうか。

Python、Rustの最低限の文法が理解できている前提

試験で使われそう(≒基本的)な型

いくつか編入の過去問を見た感じここらへんが使われそう。intとfloatはPythonのそれとまんま同じだし、charはRustにもあるので大丈夫そう。doubleもRustのf32とf64の関係みたいな感じかな?
voidというのはCとかC#にある、「型がない」ことを示す型だそう。 (Cは関数定義のときに引数と返り値の型指定が必須)
初めて触れた言語がpythonだからかリストとか辞書がないのに軽く衝撃を受けた。

変数定義

型 変数名 = 値;
int a = 1;
int b;

Rustだとこうなる

let a: i32 = 1; 
let b: i32;

関数定義

返り値の型 関数名 (引数1の型 引数1, ..., 引数nの型 引数n) {
    処理内容;
}

int x2(int input) {
    return input * 2;
}

ちなみにC言語はmain関数がないと動いてくれない。このx2関数を動かす場合、

#include <stdio.h>

int x2(int);

int main() {
    int num = 1;
    int result = x2(num);
    printf("%d", result);
    return 0;
}

int x2(int input) {
    return input * 2;
}

このようにしないとエラーが出る。3行目のプロトタイプ宣言という記述があるとmainの前にx2関数の情報が宣言されているので、x2関数が未宣言とエラーが返されずに済む。 あとmain関数で最後にreturn 0;を書くのはシェルにプログラムが正常終了したのを伝えるためみたい。