Neunomizuの日記

俺だけが俺だけじゃない

# C++でSplit関数もどき

tags: 情報

Pythonのsplitメソッド

Pythonにはsplitという組み込みメソッドがあり, これを使うことで指定した区切り文字で文字列を区切ることが出来ます

例えば

txt = "eeic is wonderful"

x = txt.split()

print(x) # ['eeic', 'is', wonderful]

このようにデフォルトでは区切り文字が(空白)になっていてこれによって区切られた文字列をリストに入れて返します

split的な関数

似たような関数をC++で書いてみようかなと思って書いたのが以下の通り

#include <iostream>
#include <string>
#include <vector>

void split(std::vector<std::string>&, std::string&, char);

int main(void) {
  std::vector<std::string> res;
  std::string input;
  char splitter;
  std::cin >> input >> splitter;
  split(res, input, splitter);
  for (auto& str : res) {
    std::cout << str << "\n";
  }
  return 0;
}

void split(std::vector<std::string>& strContainer, std::string& inputStr,
           char splitter = ' ') {
  std::string cur_str = "";
  for (char& ch : inputStr) {
    if (ch == splitter) {
      strContainer.push_back(cur_str);
      cur_str = "";
    } else {
      cur_str += ch;
    }
  }
  if (inputStr.back() != splitter) strContainer.push_back(cur_str);
}

split関数は引数に「文字列を格納する配列」, 「分割する文字列」, 「区切り文字」を取り, 分割した文字列を返り値とはせずそのまま引数の配列に入れるという風にしました

実行するとこんな感じです(ファイル名はsplit.ccとして)

$ g++ -o split split.cc
$ ./split
eeic,is,wonderful
,
eeic
is
wonderful

もっとスマートに書けないものかなと思いましたが, C++の練習ということで

github.com

# Sunk CostとしてのAtCoder(緑色で引退)

tags: 情報

要約

  • AtCoderはデータ構造とアルゴリズムを勉強するのに効率が良いと思って始めた
  • 効率よく勉強するのが目的なら他によい手段が見つかった
  • AtCoderは良いサービス
  • LeetCodeの方が自分には向いてそう

始めた理由

始めたのはデータ構造とアルゴリズムに詳しくなりたかったからです

加えて, 周りのやっている人が強い人だったからというのもあります

(自分への)実際の効用

AtCoderを始めた時は本当にプログラミングが出来なかったのですが, 始めてコードを書く量が増えたので前よりはできるようになりました. 当たり前ですね

加えてデータ構造とアルゴリズムにも詳しくなりました. 試験勉強で「あ, これAtCoderで見た!」となったこともあります

やめた理由

  • AtCoderのレートで疲れた
  • データ構造とアルゴリズムを勉強するなら他のサービスの方が良いと思った
  • これをやっている時間を他のことに使いたい
  • 新ABCからレートが上がらないのでつまらない(笑)
  • 数学(パズル?)みたいな問題が出てイライラするから(笑)

英語試験と似ている?

AtCoderというのは英語の資格試験と似ている点が結構あって

  • レート(スコア)が高いとプログラミング(英語)そのものができると思われやすい
  • レート(スコア)が目的化してしまう
  • 試験に出せるものはできるようになるがそれ以外は試験勉強ではできるようにならない

最後は語弊がありそうなので付け加えると, AtCoderの問題を解くとAtCoderの問題は解けるようになるけどWebアプリケーションが作れるようにはならないというだけで別に役に立たないということが言いたいわけではないです

逆に異なるのは

  • 英語が母国語の人は資格試験のスコアがすごいことになる
  • 無為にプログラミングをしているだけでは少し対策した程度ですごいスコアは出ない

僕はこの無駄にレートに拘るというものをしていたのですが, レートが上げて何がしたいのかと冷静に考えると特にないのでやめることにしました

TOEICの点数にめちゃくちゃこだわっている人もいますがそんな感じでしたね. 大してやっていないけど気にして脳のリソースが奪われるやつ

やめるけど

緑色の自分が言うのもなんですが役には立つと思います. やっていなかったら出来なかったであろう処理もあります

しかし, レート(水色になってやめたかった気もする)とかかけた時間を考えるとやめるのもなぁと思いながらダラダラやっていたのですがそうやってもっと大事なことをやらない方がもったいないのでやめるだけです

今はLeetCodeをぼちぼちやっています. それは僕がそっちの方が自分にとって効率的だと思っただけです. AtCoderは素晴らしいサービスだと思います(ついでにAtCoder Jobsも使うつもりなので頭上がらないです^^)

個人的には海外の面接で使われる問題をそのままパクっていて学習的なサイトであるLeetCodeの方がいいと思いました

LeetCodeの宣伝

LeetCodeの宣伝をします(冗談です)

このサイトは主にアメリカ企業のコーディングテストを突破するために作られていて, 体系的にデータ構造とアルゴリズムを学ぶことが可能です

僕がLeetCodeで気に入った点は以下の通りです

  • AtCoder的な数学ぽい問題は(少なくとも僕とその周辺の意見では)少ないです
  • 体系的にデータ構造とアルゴリズムが学べる
    • Learnというのデータ構造やアルゴリズムに関する問題が体系的にまとまったページがあります(下の通り)
    • 問題にもタグが付いているので自分の関心があるものを重点的に学ぶことができます
    • AtCoderではそういうサービスはないのでこれが大きいです

  • 問題もこのように問題ごとに難易度も書かれていて使いやすいです
    • AtCoderにはこういう自前のサービスがなくUIも見やすいです
  • 実際にコーディングテストに出題された問題が大半なので就職の役に立つ(らしいです)
  • 課金するとコーディングテストの役に立つコンテンツが使えるようになる
    • 質が高いものならお金を払ってもいいと思っています
    • 解説がわかりやすいです. 一部有料ですがこのわかりやすさなら納得です
  • discussionというユーザーが解法を共有する空間がある
    • 公式の解説が分からなくてもなんとかなります
  • 時間計算量だけでなく空間計算量の改善も重要である
    • AtCoderでは時間計算量だけが問われることが多いですが, 空間計算量も考慮するようになりました
  • 解説の動画がYoutubeにたくさんある
    • B2Bがわかり易すぎる(ちなみにYoutubeにはKruscal先生も登場します)
    • AtCoderの解説放送とは違い編集もされていて, 言語が強いと量と質も違うのだなあと思いました(丸)

他にもあると思いますがぱっと思いついたのがこれですね. ただ, 複合的な問題は出てこないですし, アルゴリズムの実装がメインです. 結構毛色が違いますねやはり

コンテストも一応開催していますが, 自分は出ていません. まだ問題解き始めた段階ですしね

手段と目的

プログラミングは手段に過ぎないと考えている人とそれ自体が目的の人がいると思います. 僕にとってはどちらでもあり目的となるプログラミングはAtCoderでのプログラミングではなく, 手段としてもAtCoder社の提供するもの以外にもっと良いと思うものがあったというだけです

某氏曰く「水色ならば人間だ」だそうですが, こういうレートが正義みたいな意見が強いのも苦手だったのでちょうどいい機会でした

(このブログを読んだchokudai氏とそのファンネルにあれこれ言われそうだ...)

# プログラミングで詰まっている所

tags: 情報

satoru-takeuchi.hatenablog.com

mizchi.hatenablog.com

流行りなのかな?ということで考えてみます

プログラミング歴

こんなのでも情報系の学部に所属しています

理解した気になっていた所

ポインタとかアドレスとか配列とか

コンピュータのメモリ上に変数などが置かれると分かるまではよく分かっていなかった

文法的に*&が付くものだと思っているだけで本質的に配列が何なのかなどを理解していなかった

理解した気になっている所

型推論

型推論と動的型付けの違いがよく分かっていないと思う

ふわふわとした「型推論」について話しているだけ

GC

「RustはGCがない言語です. 素晴らしいです」

うん. GCはコストのかかる処理だからない方が良いかも知れない. でもなんで他の言語はそんなにコストがかかるのにGCがあるのだろう. 書きやすいから?

そもそもGCがのコストって実装もしたことがないからなぁ...分かりたいと思っている

関数型プログラミング

OCamlLispも書いたことはあるけど, それで関数型プログラミングを理解したことにはならないと思う

本質みたいなのはまだよく分かっていない

これがわかりやすかったけど, 結局大規模なソフトウェアを書かないと本当の良さは分からなそう

ラムダ計算

無名関数は使っていて便利だとは思うけど, それがどう数学的に有用なのかは分かっていない

OOP

オブジェクト指向がメモリをどう使っているか, 抽象化をしているかはわかった気になっているだけで, 結局コードを書かないと分かった気になったままだと感じている

Web

Web系の会社でインターンしたのに未だによく分かっていません

ソフトウェア

ソフトウェアを作るってなんなのだろうか

そもそもソフトウェアが何なのかよく分かっていない

今の自分にアドバイス

コードを書く

コードを書かないと理解は出来ない

自分ではアルゴリズムを理解した気になっても, 本当にそれが理解できたかはコードに落とせるかで判断した方が良いという話

後, 書けるコードばかりを書いていっても書けるコードは増えないので, ある程度書けるようになったら他人のコードを読むことを覚えたほうが良いと思う

理屈も勉強した方が結局早い

理屈とはハードウェアの話から実際にどうソフトウェアで実装されているかまで

コンピュータ・アーキテクチャとOSまで理解できたらC言語とか別にいらない気がするし, 他もおそらくそう

でも理屈を勉強するのと一緒にコードを書くのが一番なのでそれをやるべき

分かっていないのが分かってきてからが本番かも知れない

最初は分かったと思い込んでしまうものだけど, それが徐々に分かってないに変わる時が来てからが本番なのかも

分かったつもりで色々話してしまうのが初心者ならば(周りを見ていてもそうだけど), 分かったつもりでいることを分かったことにできるのが中級者な気がする

上級者は人に分からせるのかな. 例えばC言語を作ったりして.

まとめ

プログラミングを2年近くやっていてもこのザマなのだけど, 未来の自分がどうにかしてくれるはずだと思って生きています

C言語における*charとchar[]の違い

tags: 情報

pointer & array

C言語では変数を宣言する際にchar*と使うものとchar[]があります

「これにはなんの違いがあるの?」という人もいそうです.かくいう自分もそうでした

ということで図示すればわかりやすいだろうということで図示したのがこちら

char*はあるarrayを指すポインタ,つまりなんらかの定数が入っています

一方,char[]はarrayそのものです

multi dimentional array

これが多次元になるとどうなるんでしょうか?

以下のようになります

char *names[]はある配列を指す複数のポインタが中に入ったarrayです

char nemes[][10]は長さ10の配列をsubarrayとして持つarrayです

まとめ

単にhttps://www.draw.io/が試したかっただけです

Rust以外無意味

tags: 情報

この記事はeeic (東京大学工学部電気電子・電子情報工学科) Advent Calendar 2019の11日目の記事として書かれました

Rustとは

公式HPによれば

  • Performance
  • Reliability
  • Productivity

という長所がある言語なようです

下のように

Rust以外は無意味なのです

C++でバリバリにコードを書いたことがないとそのありがたみがわからないよ的な文章も読みました

しかし,そう言われても実際にどういうものかわからないとこれが真実かイキリかどうか見分けがつきません

ということで実際にドキュメントを読んで短いコードを書いてみることにしました

どのドキュメントを読んだのか

公式のレポジトリにあるものを読みました

の3種類があります

Nightly(毎日更新)->Beta(6週間でNightlyから昇格)->Stable(6週間でBetaから昇格)という風になっているようです

私はStableを読みました

日本語版は2018年Editionに対応していないのでまだ読まないほうが良いと思ったので英語の方を読みました

読んだ感想

  • 競プロをするだけなら1~9章と10,12,13,18章を読んで後は他人のコードを見ながらコードを書けばなんとかなりそうな気がします
  • コンパイラがめちゃくちゃ賢い色々なパラダイムを含む言語だと思いました
  • CやC++を書いたことがない人(メモリに対する知識がない人)がいきなりRustを書くのは大変な気がします
    • Rustでコードを書こうとするとownership,borrowing,lifetimesを常に意識する必要がります
    • これらを理解するのにメモリに関して具体的にイメージできる必要があるように思えます
      • そして,そういう勉強をした人はCやC++を学んだ経験があるはずなので初心者がやることがそもそもなさそうに感じます
      • 初心者がやるとコンパイラが優しすぎて,挫折してしまいそうです
    • という理由で文系学部などから来たWeb系に来た人がいきなりこれを書くのも大変そうだなという感想を持ちました
  • バグの温床になりそうな部分を片っ端から潰している言語で使えるようになったら楽しそうです
  • Rustで書いている人が強いという話は本当っぽいなぁと思いました(ただなんちゃってみたいな人は除く)

読んでとりあえずコードを書く

Rustという言語の強みはコンパイラと言いました.これは本当でコンパイラが少しでもバグの原因になりそう!というコードを見つけたら教えてくれます

ということで適当に書いてみます

フィボナッチ数

プログラミング言語の勉強をする時,最初は再帰関数の練習としてフィボナッチ数を書きますよね(?)

ということで次のような感じで0~10のフィボナッチ数は書けます

fn main() {
    println!("!Fibonacci!");
    for i in 0..11 {
        println!("{}th Fibonacci number is {}", i, fibonacci(i));
    }
}

fn fibonacci(n: u32) -> u32 {
    if n == 0 || n == 1 {
        1
    } else {
        fibonacci(n - 1) + fibonacci(n - 2)
    }
}

実行するとこんな感じに出力されます

!Fibonacci!
0th Fibonacci number is 1
1th Fibonacci number is 1
2th Fibonacci number is 2
3th Fibonacci number is 3
4th Fibonacci number is 5
5th Fibonacci number is 8
6th Fibonacci number is 13
7th Fibonacci number is 21
8th Fibonacci number is 34
9th Fibonacci number is 55
10th Fibonacci number is 89

このコードを見ると

「あれ?そんなに難しくじゃん」

と思うかも知れません.しかし,後ほんの少し複雑なコードを書くと困ったことになります

Hashmapを使った問題

LeetCodeで一番解かれている問題を使います

めちゃくちゃ簡単なこの問題ですが,Rustで書くと落とし穴が何個か有ります

問題の概要はある整数列が与えられその値から2つの異なる値を足し合わせてtargetという目的の数になること

解答は

  • map<target - num0, num1>というHashMapを作り,この値があればそれを出力し,なければ新しくkeyとvalueを新しく格納する

というものです

下のように書きました

use std::collections::HashMap;
impl Solution {
    pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {        
        let mut map: HashMap<i32, i32> = HashMap::new();
        let mut res: Vec<i32> = Vec::new();
        
        for (i, num) in nums.iter().enumerate() {
            if (map.contains_key(&(target - num))) {
                res.push(map[&(target - num)]);
                res.push(i as i32);
                return res;
            }
            map.insert(*num, i as i32);
        }
        return res;
    }
}

この短いコードの相田に以下のような点に注意する必要が有ります

  • map.contains_key(&(target - num))に関して
    • ここではHashMapの仕様上&を付けてborrowする必要がある(他はダメ)
  • map.insert(*num, i as i32)に関して
    • numnums.iter().enumerate()から取り出しており,borrowされており,*を付けてdereferenceをしないとinsertできないため(これはHashMapinsertする際にownershipが移ることから)
    • i as i32iのままだとusize(符号なし)というデータ型なので出力形式とマッチしないから

このように実行時のバグをなくすためにコンパイル時に指摘しまくります

自分もあまり理解しておらず理解が浅いうちはコンパイラにお世話になりっぱなしだと思います

結論

Rust以外無意味ではなくてRust(が書けないエンジニアはコンピュータを分かったつもりで書いているので)無意味ということなのかなと思いました

C++やCをわかっていない人が書いても確かに恩恵が分かりにくそうだと感じました.というのも一度メモリに触れておかないとコンパイラに頼りっぱなしでごちゃごちゃやって理解した気になってしまいそうだと感じたからです

基本大事ダネ☆

次回

C言語以外無意味

続く...