Tag: C++

C++の備忘録#2: 配列の中身チェッカー、CSV入出力のあれこれ

こんにちは。寒くなるにつれて頭痛の酷さが増してきている気がするRockinWoolです。運動不足が影響しているのではないかと思って今日は外を歩いてみたり、2年ぶりくらいにカラオケをしてみたり、風呂に長めに入ってみたりしたけれど効果はありませんでした。仕方無いのでC++の備忘録の第二弾をやって少しでも成長を感じていこうと思います。 配列の中身チェッカ 関数のオーバーロードの練習を兼ねて作ったプログラムです。もし引数がvector<int>だったら、その中身を1行で出力します。vector<vector<int>>だったら全要素を順番に表示します。まずは引数がvector<int>だった場合。 解説するほどの場所は無いかもしれませんが、vec.size()で配列の長さを取得しているところが工夫ですかね。次はvector<vector<int>>の場合。 同じvector_checker()という名前ですが、引数が2次元だとこちらが呼ばれます。工夫はvec.size()-1の範囲でコロンを打つようにしている部分ですかね。これらのプログラムは挙動が想像しやすいように書けたと思うのでGood jobだと思います。 失敗事例: vectorでは無い場合 次はバグを生んだプログラムの紹介です。下記のプログラムのバグを見つけてみましょう。 このプログラムを回した際の出力は1,2,3,4,5,0,937289472,-494252404,になります。何か知らない数が生まれてきていますね。ちなみにこれをChatGPTに相談してみたところの回答が下記になります。 The issue you’re encountering is related to how arrays are passed as pointers in C++. When you pass an array…

【Atcoder日記】ABC #329のメモ

こんにちは。最近Pythonとc++の連携ができるようになってからc++の技術が上がってきたと感じているので、今日は久し振りにAtcoderに手を出してみました。Python軸でAtcoderに参加していた時は、制限時間の壁があまりにもキツすぎて解けているのに間に合わないということがよくありました。その点C++であれば問題ない!はず。結果としては60分でABCの3問正解という感じでした。(頭痛のせいで60分以上の作業は正直きつい)それでは、RockinWool自己流の解答をご笑覧あれ。 A問題(Spread) 難易度:超かんたん 入力文字に対して、スペースを開けて返答しろという非常に簡単な問題。A問題の中でも過去一簡単なのでは?と感じました。c++のstringの使い方さえ間違わなければ大丈夫。それと最後の文字以降にスペースを入れてはいけないとのことだったのでif文で分岐させました。(個人的には、この難易度でも20行近く書く必要のあるc++って不便だなと感じるなど) B問題(Next) 難易度: 普通 B問題はごく普通の難易度。もしかしたら過去と比べて簡単な方かもしれない。最大値の次に大きな数を答えるということで、最大値が複数ある場合にも対応できるかどうかが問われている。今回はvectorとsortを使って、入力を降順に並び替えたのちに最大値よりも小さくなった瞬間の数を返答するようにした。 C問題(Count xxx) 難易度: 比較的簡単 C問題は過去と比べかなり簡単な方かも。文字の組み合わせを答えろと言われているが、要は連続した同一文字からなる文字列の長さを足し合わせれば良い。例えばaaabbccだと3+2+2=7という感じ。問題はaaabbaaだと3+2=5となる点(aは3と2のパターンがあるが、大きい方を採用する)。この考え方を実現するにはa:3,b:2のようなpythonで言うdictionary型のデータが必要であることに気づく。ということはunordered_map<char,int>といったデータを用意して代入していけば良い。 この解答で少し不満なのは、ループ部分でi番目の文字とi+1番目の文字を比べている仕様上、最終文字とその直前が違う場合にif文を組まなければならない点。例えば最初のif文では入力が1文字である場合の例外を設けているし、以降にもif(i==inputstr.size()-2)の時に分岐するようにひと手間かけている。もう少し美しく書けたかもしれないが、制限時間中には思いつかなかった。しかし、過去のC問題に比べると例外処理の少なさから、非常に簡単な問題だったと感じる。ただ、個人的にはABC正答できたのは嬉しい。 結び 今回の問題は結構簡単に感じましたが、もしかしたら自分の能力が上がってきているのかもと自信が付きました。これからもpythonとc++をどんどん連携させて、c++をバリバリ書けるようになっていきたいですし、それを記事にしていこうと思いますので温かい目で見守ってください!それでは、よいプログラミング生活を!

C++の備忘録: 乱数生成、配列(vector)、連想配列(map)

こんにちは。11月に入ったら急に頭痛が復活してきてしまったRockinWoolです。去年くらいから冬服のようなピッチリとして重い服を着ると、肩こりが悪化して頭痛が発生する気がしています。同じような症状の人がいたら嬉しいですが、この症状がある限りスーツを着て仕事するのは無理なんじゃないかと思ってしまいます。まあ、それ以前に職場で壊したメンタルを治すところからなんですが。さて、今回のテーマはC++で詰んだポイントの備忘録まとめです。前回の記事で紹介したpybind11を使ってpythonからc++で作成したバイナリを叩けるようになってから、ずーっとc++でコーディングしてました。これが結構楽しいけれど、反面覚えることが多くてpythonほど簡単には書けない・・・。そんなこんなで備忘録を残していつでも振り返られるようにしておこうと思います。 乱数生成 まず最初にびっくりしたのがc++での乱数生成のクセの強さ。pythonだとrandom.randn(min,max)一行で書けるような内容がc++だとそうもいかない。結局自作でrandnに相当するものを作成することになりました。 この関数を作成した際に引っかかったのはstd::uniform_int_distribution distribution(min, max-1);のmax-1の部分。実はpythonのrangeとかと違って、min≦乱数≦maxと出てきてしまうのです。したがってmax-1としないと挙動が異なってしまいます。というか、c++でもforループを組むときはfor(int i=0; i<N; i++)みたいに0~N-1の範囲で回すのに、なんで乱数を生成する関数だけ仕様が違うんだ・・。ということで、範囲外アクセスを生み出して一敗しました。チキショー。 配列(Vector) c++の配列であるvectorは、慣れれば結構使いやすいと感じています。こいつのすごいところはpybind11経由でpythonから呼ばれた際に、c++側関数の返り値をvector<int>やvector<vector<int>>としてもpython側からlist型として認識されるところ!(これってどっちかというとpythonがすごいんだけど)。逆にc++で自作の構造体をstructで作って、それを返り値としてpythonに渡そうとしたら失敗しました。やっぱり1関数1機能の規則に則って一度に複数個の返り値を返さないことが大切ですね。ちょっとクセが強いなと思ったのは、初期化時の表現。例えば下記のようなプログラムの場合、jam_counterはN個の要素すべてが0で初期化された一次元配列になります。 要素の呼び出し方はCやPythonと同じようにjam_counterといった感じになります。その他、vectorで怖いなと思ったのはpush_back関数。 上記はvector<int>型のdeclared_locに要素を”追加”している命令。配列の長さが後から変わるって結構怖くないか・・?ってC経験者だと感じてしまいます。Pythonのlist型にもappendとかあったけど、あれはメモリ管理とかまったく気にしなくても良かったので安心して使ってましたけど、c++のvectorもあれと同じくらい信用してもいいのかな〜。 連想配列(map,unordered_map) vector<int>がlist型に対応しているのに対して、こちらはdictionary型に似ていると感じました。デフォルトのmapだとKeyの順序まで覚えるらしいけど、ほとんどの場合はそこまで記憶しなくて良いのでunordered_mapで良いかなと感じています。使い方は下記のような感じ。 第一引数のintはKeyのtypeを指定し、第2引数は値のtypeを指定している。今回は整数を指定すると、それに応じた配列が返ってくるような連想配列型elementPositionを定義したということになる。これだけだとCの構造体でも同じことができそうだけど、連想配列型はc++のautoを用いたループと相性が良い。例えばこんな感じ。 上記は引数のvector<int>の要素中に同じ整数が含まれていたら、重複を報告するような関数。要素ではなく要素の位置番号をvalue, そしてKeyはvector<int>の各値を持つことによってこれを実現している。注目したいのはconst auto& entry : elementPositionsの部分。const宣言によってループのbodyではentry変数が変化しないことを保証し、autoによって連想配列型であることを実行時に勝手に判定してもらう。そしてauto&であることからelementPositionsを参照して使用することが指定されている。こうすれば、構造体では難しかった全Keyや全Valueの探索が超!楽ちん。今回もconst auto&によって全valueをチェックして重複があるかだけを確認できる。これ、pythonでも実装は結構難しいかなと感じているので結構すごいなと思っています。 まとめ C++はクセが強いけれど、できることも結構すごいのではないか?と感じ始めました。pybind11を使えばpythonの便利なところとc++の高速さを両立できるので革命が起きています。また、何かしらの備忘録を残そうと思っていますので、良ければ記事への励ましコメントなどをよろしくお願いします。モチベアップに繋がります。もちろん、記事で間違った内容などがありましたらご指摘よろ。それではまた次回~~~。

【pybind11】Pythonが重すぎるのでC++に手伝ってもらう

こんにちは。RockinWoolです。今日はpybind11を使ってpythonの一部処理をC++に置換して高速化することを目指します。私はかねてからPythonの処理があまりにも遅すぎることに悩んでました。このせいで、実験がうまくいかなかったり、プログラムの完了までに1ヶ月近い時間がかかったりしていました。ChatGPTにこのことを相談したらpybindが初心者向けで良いとの回答をもらったので、今回はこの悩みを解消すべく環境構築していきます。 pybind11の導入 今回はanaconda環境に導入する方法を示します。pipで管理している人はcondaコマンドやLinuxのPATH情報を適宜入れ替えて貰えれば問題無いと思います。まずはpybind11をcondaでインストールします。 ビルドの設定 次にCMakeLists.txtの準備をします。余談ですが、今回作成するプログラムはnetworkxで作成した複雑ネットワークを取り扱うことを考えています。ですので、C++のファイル名はload_network.cppとしてtoolsetライブラリ内に配置するとしました。つまり、CMakeLists.txtから見て下記のような配置になっています。 CMakeLists.txt|-build |- load_network(ビルド後生成物。pythonで呼べる)|-toolset |-load_network.cpp この状態におけるCMakeLists.txtはこんな感じになります。condaのbase環境にpybind11を入れているので、特定の環境に入れている場合はenvs//includeのように置き換えてください。 cmake_minimum_required(VERSION 3.2)project(pybind_test VERSION 0.1.0)find_package(pybind11 REQUIRED)find_package(Python REQUIRED)include_directories(/home//anaconda3/include)include_directories(/home//anaconda3/include/pybind11)link_directories(/home//anaconda3/lib)pybind11_add_module(load_network ./toolset/load_network.cpp) C++プログラムの本体 そしてload_network.cppの本体を下記のようにします。 1行目はpybind11を使うために必要な呪文、2行目はpythonのリスト型を扱うために必要な呪文という感じです。プログラム的には引数をそのままリターンする関数なので特別な処理はしていません。PYBIND11_MODULE(load_network, m)以降は完全に元記事を参考にしただけなので意味は良くわかっていませんが、ビルド後に関数呼び出しをする際に必要になる設定だと思っています。上記のload_network.cppをCMakeLists.txtを使ってビルドすればload_network~~.soができるので、これをtest.pyと同じ階層においてtest.pyから呼び出せば機能します。なので次はtest.pyを作りましょう。 呼び出し元のPythonプログラム あまり言及することはありませんが、c++で作ったライブラリが存在していると仮定してそのまま実装すれば機能します。す、すごい! まとめ pybind11はcondaコマンド一発でインストールできるので、かなり使いやすいと感じました。また、cmakeに馴染んでいることが条件になってきそうなので、c++と併せて勉強しなければと感じました。これを使って自作ライブラリとか作れればかっこいいなとか思いつつ、次回に向けて精進します。ここまで読んでくださってありがとうございました。

DBMSを触りたい!②

こんにちは。そのまま続きをやっていきます。前回は下記のようなコマンドで何か全権限を与えた時点でPCを落としてました。 ここで実際にユーザが追加されているのか?そして、権限がどうなっているのか?これを確認する必要がでてきます。これに関しては「わくわくBank」さんに詳しく書いてあったので、そのままやっていこうと思います。まずは、現在登録されているユーザを出していきます。 パッと見た感じ、UserにRWWが登録されていること、hostはlocalhostになっていることがわかります。ちなみにSELECT * FROM mysql.user; と入力することでuserやhost以外の列も出力できます。全然意味がわからないものがほとんどなので、そのうち理解して行きたいですね。 また、権限周りを確認するために、下記コマンドを入力してみました。 どうやらここでは%の部分がエラーになっているようで。%は正規表現か何かだと思って元から変更しなかったのですが、元記事ではlocalhostではなく%にしていたので、こういう表記になったようだ。そこで、localhostを指定することによって、うまく確認することができた。 ちなみに、前回最新IT技術情報_arkgame.comさんの記事では、GRANT ALL PRIVILEGESした後にFLUSH PRIVILEGESを実行して権限付与を確定させていただのだが、筆者の場合はFLUSH PRIVILEGESせずにPCの電源を落とすだけで権限付与が成功していた。これについてはMy Opinion is my ownに詳細な記載があったため、知りたい方はぜひ見に行ってくださいね。 3. データベース内でユーザが切り替えられない せっかくユーザを作成して権限を付与したので、これからそのユーザになってテーブルを作ってやるぞー!となった矢先、mysql内でcurrent userを切り替える方法が探しても見つからない。これは何故だ?と思って同じような質問を見に行くと「クライアントに何を使用しているのか不明ですが、利用ユーザは MySQL への接続の際に指定しているので、一般的には一度接続を解除して、その後、再度別ユーザを指定して接続しなおします。」との回答が。なるほど、そういう思想なんですね。 ということで、一旦EXITを入力してmysqlから脱出した後、今度はRWWとしてログインすることにします。 これによって、確かにRWWとしてログインできたようです。 4. データベースの作成 未だにデータベースという概念が分かっていませんが、テーブルデータの塊のことを指すのでしょうか?一応ChatGPTくんもDBはTableを束ねる上位存在というようなことを言っているので、その認識のまま進めていきます。 ちなみに今回扱えるようにしたかったデータはテーブルデータだったため、正直な話データベースを構築して、そこからテーブルに参照させることの意義が理解できていないです。そんなこんなでテーブルから作ろうとしたら怒られたので、愚直にDBから作って行きます。…