├── .gitignore ├── README.md ├── articles ├── .keep ├── 170e2fb40494e6c265e5.md ├── 385c64aab18a0a54df68.md ├── 91b64001e8e0a1ac0c30.md ├── 9d566e93edfb4f95bb50.md ├── competitive-programming-modinv.md ├── conky-with-i3bar.md ├── control-flow.md ├── double-binary-search.md ├── equilibrium-simulation.md ├── hiragana-substring.md ├── i3-window-manager.md ├── latex-github-actions.md ├── line-break-in-string-literal.md ├── niu-graph-rust.md ├── parse-infix-notation.md ├── rust-for-beginner.md ├── rust-graph-trait.md ├── rust-pitfalls.md ├── rust-transpose.md ├── satanic-fourier.md ├── so-what-is-a-pointer.md └── tex-vs-satysfi.md ├── books ├── .keep ├── apg4b-rust-ver │ ├── config.yaml │ ├── cover.png │ ├── cover.svg │ ├── images │ │ ├── arithmetic_right_shift_negative.png │ │ ├── arithmetic_right_shift_positive.png │ │ ├── array_reference.png │ │ ├── array_reference.svg │ │ ├── bitwise_and.png │ │ ├── bitwise_not.png │ │ ├── bitwise_operation.svg │ │ ├── bitwise_or.png │ │ ├── bitwise_xor.png │ │ ├── format.png │ │ ├── format.svg │ │ ├── left_shift.png │ │ ├── logical_right_shift.png │ │ ├── memory.svg │ │ ├── pattern.svg │ │ ├── pattern_array.png │ │ ├── pattern_complex.png │ │ ├── pattern_ref_mut_tuple.png │ │ ├── pattern_ref_single.png │ │ ├── pattern_ref_tuple.png │ │ ├── pattern_reference.png │ │ ├── pattern_struct.png │ │ ├── pattern_struct.svg │ │ ├── pattern_tuple.png │ │ ├── pattern_tuple_struct.png │ │ ├── quick_sort.svg │ │ ├── quick_sort_split.png │ │ ├── reference1.png │ │ ├── reference2.png │ │ └── reference3.png │ └── message.md ├── llvm-toy-language │ ├── 01-intro.md │ ├── 02-error.md │ ├── 03-token.md │ ├── 04-lexer.md │ ├── 05-expression.md │ ├── 06-parse-expression.md │ └── config.yaml ├── rust-atcoder-old │ ├── 01-intro.md │ ├── 02-environment-setup.md │ ├── 03-hello-world.md │ ├── 04-arithmetic.md │ ├── 05-variable.md │ ├── 06-literal.md │ ├── 07-input.md │ ├── 08-if.md │ ├── 09-block.md │ ├── 10-assert.md │ ├── 11-tuple.md │ ├── 12-array.md │ ├── 13-format.md │ ├── 14-reference.md │ ├── 15-for.md │ ├── 16-pattern.md │ ├── 17-mutable.md │ ├── 18-mutable-reference.md │ ├── 19-vector.md │ ├── 20-loop.md │ ├── 21-slice.md │ ├── 22-match.md │ ├── 23-string.md │ ├── 24-ownership.md │ ├── 25-function.md │ ├── 26-call-by-reference.md │ ├── 27-boolean.md │ ├── 28-recursion.md │ ├── 29-bitwise-operation.md │ ├── 30-time-complexity.md │ ├── 31-generic-function.md │ ├── 32-returning-reference.md │ ├── 33-structure.md │ ├── 34-enumerated-type.md │ ├── 35-method.md │ ├── 36-generic-structure.md │ ├── 37-option.md │ ├── 38-result.md │ └── config.yaml └── rust-atcoder │ ├── 01.intro.md │ ├── 02.setup.md │ ├── 03.hello-world.md │ ├── 04.arithmetic.md │ ├── 05.variable.md │ ├── 06.literal.md │ ├── 07.input.md │ ├── 08.if.md │ ├── 09.block.md │ ├── 10.assert.md │ ├── 11.tuple.md │ ├── 12.array.md │ ├── 13.format.md │ ├── 14.reference.md │ ├── 15.for.md │ ├── 16.pattern.md │ ├── 17.mutable.md │ ├── 18.mutable-reference.md │ ├── 19.loop.md │ ├── 20.label.md │ ├── 21.function.md │ ├── 22.crate.md │ ├── 23.recursion.md │ ├── 24.vector.md │ ├── 25.ownership.md │ ├── 26.slice.md │ ├── 27.call-by-reference.md │ ├── 28.match.md │ ├── 29.string.md │ ├── 30.boolean.md │ ├── 31.bitwise-operation.md │ ├── 32.time-complexity.md │ ├── 33.generic-function.md │ ├── 34.returning-reference.md │ ├── 35.structure.md │ ├── 36.enumerated-type.md │ ├── 37.method.md │ ├── 38.generic-structure.md │ ├── 39.option.md │ ├── 40.result.md │ ├── 41.trait.md │ ├── 42.struct-with-reference.md │ ├── 43.closure.md │ ├── 44.iterator.md │ ├── config.yaml │ └── cover.png ├── images ├── array_reference.png ├── array_reference.svg ├── heap-allocate.png ├── heap-allocator.png ├── heap-deallocate.png ├── heap.svg ├── option-path-dp.png ├── option-path-dp.svg ├── option-path-dp2.png ├── option-path-dp3.png ├── option-path-dp4.png ├── reference_memory_1byte.png ├── reference_memory_4byte.png ├── stack.png ├── stack.svg └── stack_function_call.png ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zenn Contents 2 | 3 | * [📘 How to use](https://zenn.dev/zenn/articles/zenn-cli-guide) 4 | * [📘 Markdown guide](https://zenn.dev/zenn/articles/markdown-guide) -------------------------------------------------------------------------------- /articles/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/articles/.keep -------------------------------------------------------------------------------- /articles/170e2fb40494e6c265e5.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Twitter API を使って Rust からツイートする" 3 | emoji: "🐦" 4 | type: "tech" 5 | topics: ["rust", "twitter", "oauth"] 6 | published: false 7 | --- 8 | # これは何 9 | Twitter API を使用してツイートを投稿するプログラムを, Rust で書く記事です.[公式の解説](https://developer.twitter.com/en/docs)と[この記事](https://qiita.com/hppRC/items/05a81b56d12d663d03e0)を参考にしながら書いています. 10 | # Crates 11 | 使用クレートは以下の通りです: 12 | | crate | version | features | 13 | | - | - | - | 14 | | base64 | 0.13.0 | | 15 | | chrono | 0.4.19 | | 16 | | hmac-sha1 | 0.1.3 | | 17 | | percent-encoding | 2.1.0 | 18 | | rand | 0.7.3 | | 19 | | reqwest | 0.10.8 | | 20 | | tokio | 0.2.22 | macros | 21 | reqwest 0.10.8 が tokio 0.3.0 以降で正常に動かないため, tokio 0.2.22 以前を使用することに注意して下さい. tokio 以外は全て記事執筆時点 (2020/10/27) で最新のバージョンです. 22 | # OAuth 23 | Twitter が使用している OAuth 1.0 についての説明です. 24 | ## Keys & Tokens 25 | [ここ](https://developer.twitter.com/apps)の右上にある Create an app ボタンを押して app を作り, Details ボタンから Keys and tokens タブを開いて API key / API secret key / access token / access token secret の 4 つを手に入れます.これは手元の適当なファイルに保存しておき,後でプログラムから読み取ります. 26 | ## signature 27 | API secret key と access token secret は, signature の生成に使われます.ツイートする際にクライアントは signature base string という文字列を構成し,これと API secret key / access token secret から signature を生成します.サーバー側でこの signature が確認され,認可が行われます. 28 | 29 | signature base string , API secret key , access token secret から signature を生成する方法は次の通りです: 30 | 1. API secret key と access token secret を `&` で繋いだ文字列を signing key とする. 31 | 2. signing key を鍵として, signature base string を HMAC-SHA1 でハッシュ化する. 32 | 3. base64 でエンコードする. 33 | ## signature base string 34 | 上で登場した signature base string は,次のパラメータを用いて作られます. 35 | - HTTP method (ツイートするときは `POST`) 36 | - URL (ツイートするときは `https://api.twitter.com/1.1/statuses/update.json`) 37 | - ツイートの内容に関するパラメータ 38 | - `status`: ツイート本文. 39 | - `in_reply_to_status_id`: 返信先ツイートの ID . 40 | 41 | など(他のパラメータについては[こちら](https://developer.twitter.com/en/docs/twitter-api/v1/tweets/post-and-engage/api-reference/post-statuses-update)) 42 | - それ以外のパラメータ 43 | - `oauth_consumer_key`: API key 44 | - `oauth_token`: access token 45 | - `oauth_signature method`: twitter が使用しているのは `HMAC-SHA1`. 46 | - `oauth_version`: twitter が使用している OAuth のバージョンは `1.0`. 47 | - `oauth_timestamp`: 現在時刻.これがズレていると認可が下りないことがある. 48 | - `oauth_nonce`: ランダムな文字列. 49 | 50 | ただし HTTP method と URL 以外のパラメータは「キー: 値の説明」の順に書いています. 51 | 52 | これらのパラメータをもとに signature base string を作り,そこから signature を生成します.同時に,これらのパラメータ自体も HTTP に乗せて送ります.ツイートの内容に関するパラメータは, URL の末尾に付け加えます.それ以外のパラメータは, signature と一緒に HTTP の Authorization ヘッダに乗せます. 53 | 54 | パラメータから signature base string を作る方法は次の通りです: 55 | 1. method と URL 以外のパラメータについて,キーと値の両方をパーセントエンコードする. 56 | 2. パーセントエンコードしたキーによってパラメータを辞書順に並べ,「キー`=`値」の形式で書いて `&` でつないだものを parameter string とする. 57 | 3. method , URL をパーセントエンコードしたもの, parameter string をパーセントエンコードしたものの 3 つを,これらの順に並べて `&` でつなぐ. 58 | 59 | # 設計 60 | `struct Client` に API key / API secret key / access token / access token secret を持たせます. 61 | ```rust 62 | struct Client { 63 | api_key: String, 64 | api_secret_key: String, 65 | access_token: String, 66 | access_token_secret: String, 67 | } 68 | ``` 69 | `impl Client` 内に以下の関数を用意します: 70 | - 関連関数 `from_config()` を使って config ファイルからパラメータを読み込むようにします. 71 | - `Client` に,ツイートするための関数 `tweet()` を定義します.ツイートする文字列を `&str` で受け取ってツイートし,レスポンスを返します. 72 | - `tweet()` の中身は, method と URL と「ツイートの内容に関するパラメータ」を受け取ってリクエストを送る関数 `request()` を呼び出すだけにします. 73 | - `request()` は「ツイートの内容に関するパラメータ」を `&std::collections::BTreeMap<&str, &str>` として受け取ることにします. 74 | - `request()` は method , URL ,「ツイートの内容に関するパラメータ」を関数 `authorization()` に渡し, `authorization()` は HTTP の Authorization ヘッダの内容を `String` で返します. signature の生成は `authorization()` 内で行うため, `request()` の内部で「それ以外のパラメータ」は必要ありません. 75 | - `request()` は HTTP の他の要素も揃え, reqwest で送ります. 76 | - `authorization()` の中で, timestamp や nonce の生成を行い,「それ以外のパラメータ」を `Vec<(&str, &str)>` として揃えます. 77 | - `authorization()` は method , URL ,ツイートの内容に関するパラメータ,その他のパラメータを全て関数 `signature()` に渡し, `signature()` は signature を `String` で返します. `signature()` の中で API secret key と access token secret を使用します. 78 | # 実装 79 | 以下,一つ一つの関数を実装していきます. 80 | ## `from_config()` 81 | 指定された名前のファイルを開き, API key , API secret key , access token , access token secret の順に一行ずつ読み込みます. 82 | ```rust 83 | fn from_config(filename: &str) -> Result> { 84 | let config = std::fs::File::open(filename)?; 85 | let mut reader = std::io::BufReader::new(config); 86 | fn read_line( 87 | reader: &mut T, 88 | ) -> Result> { 89 | let mut s = String::new(); 90 | reader.read_line(&mut s)?; 91 | s.pop(); 92 | Ok(s) 93 | } 94 | Ok(Client { 95 | api_key: read_line(&mut reader)?, 96 | api_secret_key: read_line(&mut reader)?, 97 | access_token: read_line(&mut reader)?, 98 | access_token_secret: read_line(&mut reader)?, 99 | }) 100 | } 101 | ``` 102 | ## `tweet()` 103 | `tweet()` から `request()` を呼び出します. 104 | ```rust 105 | async fn tweet(&self, status: &str) -> Result { 106 | let mut parameters = std::collections::BTreeMap::new(); 107 | parameters.insert("status", status); 108 | self.request( 109 | reqwest::Method::POST, 110 | "https://api.twitter.com/1.1/statuses/update.json", 111 | ¶meters, 112 | ) 113 | .await 114 | } 115 | ``` 116 | ## `request()` 117 | `request()` は reqwest を使って HTTP リクエストを送ります.ヘッダーを作る際に `authorization()` を呼び出します. 118 | ```rust 119 | async fn request( 120 | &self, 121 | method: reqwest::Method, 122 | url: &str, 123 | parameters: &std::collections::BTreeMap<&str, &str>, 124 | ) -> Result { 125 | let header_map = { 126 | use reqwest::header::*; 127 | let mut map = HeaderMap::new(); 128 | map.insert( 129 | AUTHORIZATION, 130 | self.authorization(&method, url, parameters) 131 | .parse() 132 | .unwrap(), 133 | ); 134 | map.insert( 135 | CONTENT_TYPE, 136 | HeaderValue::from_static("application/x-www-form-urlencoded"), 137 | ); 138 | map 139 | }; 140 | let url_with_parameters = format!( 141 | "{}?{}", 142 | url, 143 | equal_collect(parameters.iter().map(|(key, value)| { (*key, *value) })).join("&") 144 | ); 145 | 146 | let client = reqwest::Client::new(); 147 | client 148 | .request(method, &url_with_parameters) 149 | .headers(header_map) 150 | .send() 151 | .await 152 | } 153 | ``` 154 | ## `equal_collect()` 155 | `request()` の中で URL の末尾にツイートの内容に関するパラメータを追加しますが,このときに次のような関数 `equal_collect()` を使用します. 156 | ```rust 157 | fn equal_collect<'a, T: Iterator>(iter: T) -> Vec { 158 | iter.map(|(key, value)| format!("{}={}", percent_encode(key), percent_encode(value))) 159 | .collect() 160 | } 161 | ``` 162 | これによって, `{"in_reply_to_status_id": "0000", "status": "hello"}` のような `BTreeMap` を `["in_reply_to_status_id=0000", "status=Hello"]` のような `Vec` に変換できて,これを `join("&")` することで `"in_reply_to_status_id=0000&status=Hello"` のような文字列が得られます. 163 | 164 | `equal_collect()` は, `authorization()` と `signature()` の中でも使用します. 165 | ## `percent_encode()` 166 | また, `equal_collect()` の中では次のような関数 `percent_encode()` を使用します. 167 | ```rust 168 | fn percent_encode(s: &str) -> percent_encoding::PercentEncode { 169 | use percent_encoding::*; 170 | const FRAGMENT: &AsciiSet = &NON_ALPHANUMERIC 171 | .remove(b'*') 172 | .remove(b'-') 173 | .remove(b'.') 174 | .remove(b'_'); 175 | utf8_percent_encode(s, FRAGMENT) 176 | } 177 | ``` 178 | `percent_encode()` は `signature()` の中でも使用します. 179 | ## `authorization()` 180 | `authorization()` で「それ以外のパラメータ」をそろえ, `signature()` を呼び出します.「それ以外のパラメータ」に signature を追加し, Authorization ヘッダを生成します. 181 | ```rust 182 | fn authorization( 183 | &self, 184 | method: &reqwest::Method, 185 | url: &str, 186 | parameters: &std::collections::BTreeMap<&str, &str>, 187 | ) -> String { 188 | let timestamp = format!("{}", chrono::Utc::now().timestamp()); 189 | let nonce: String = { 190 | use rand::prelude::*; 191 | let mut rng = thread_rng(); 192 | std::iter::repeat(()) 193 | .map(|()| rng.sample(rand::distributions::Alphanumeric)) 194 | .take(32) 195 | .collect() 196 | }; 197 | 198 | let mut other_parameters: Vec<(&str, &str)> = vec![ 199 | ("oauth_consumer_key", &self.api_key), 200 | ("oauth_token", &self.access_token), 201 | ("oauth_signature_method", "HMAC-SHA1"), 202 | ("oauth_version", "1.0"), 203 | ("oauth_timestamp", ×tamp), 204 | ("oauth_nonce", &nonce), 205 | ]; 206 | 207 | let signature = self.signature(method, url, parameters.clone(), &other_parameters); 208 | 209 | other_parameters.push(("oauth_signature", &signature)); 210 | 211 | format!( 212 | "OAuth {}", 213 | equal_collect(other_parameters.into_iter()).join(", ") 214 | ) 215 | } 216 | ``` 217 | ## `signature()` 218 | `signature()` は, method , URL ,ツイートの内容に関するパラメータ,それ以外のパラメータを全てあわせて, API secret key と access token secret を用いて signature を生成します. 219 | ```rust 220 | fn signature<'a>( 221 | &self, 222 | method: &reqwest::Method, 223 | url: &str, 224 | mut parameters: std::collections::BTreeMap<&'a str, &'a str>, 225 | other_parameters: &Vec<(&'a str, &'a str)>, 226 | ) -> String { 227 | for (key, value) in other_parameters { 228 | parameters.insert(key, value); 229 | } 230 | let parameter_string = equal_collect(parameters.into_iter()).join("&"); 231 | 232 | let signature_base_string = format!( 233 | "{}&{}&{}", 234 | method, 235 | percent_encode(url), 236 | percent_encode(¶meter_string) 237 | ); 238 | let signing_key = format!("{}&{}", self.api_secret_key, self.access_token_secret); 239 | base64::encode(hmacsha1::hmac_sha1( 240 | signing_key.as_bytes(), 241 | signature_base_string.as_bytes(), 242 | )) 243 | } 244 | ``` 245 | # ツイート 246 | これで, `main()` 関数からツイートすることができるようになりました. 247 | ```rust 248 | #[tokio::main] 249 | async fn main() -> Result<(), Box> { 250 | match Client::from_config("config") { 251 | Ok(client) => { 252 | let response = client.tweet("hello").await?; 253 | let text = response.text().await?; 254 | println!("{}", text); 255 | } 256 | Err(err) => { 257 | println!("failed to read config file: {}", err); 258 | } 259 | } 260 | Ok(()) 261 | } 262 | ``` 263 | # まとめ 264 | 全体のコードは[こちら](https://github.com/fiveseven-lambda/twitter/tree/main/twitter)です. 265 | 266 | この記事ではツイートすることだけを目標としましたが,実は上で出てきた `Client::request()` 関数に渡すものを変えるだけで,返信したり TL を読み込んだりすることもできるようになります. 267 | 268 | 私は Rust を書き始めてからまだ日が浅いので,良くない書き方をしている箇所などあるかと思います.もし気付いたことがあれば指摘していただけると幸いです. 269 | -------------------------------------------------------------------------------- /articles/91b64001e8e0a1ac0c30.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Arch Linux インストール後の諸設定集" 3 | emoji: "💻" 4 | type: "tech" 5 | topics: ["archlinux"] 6 | published: true 7 | --- 8 | 自分用のメモとして, Arch Linux をインストールしたときに自分が普段使っている環境を作るための方法を書いておきます. 9 | # zsh のインストールと設定 10 | ``` 11 | $ sudo pacman -S zsh 12 | $ chsh -s $(which zsh) 13 | ``` 14 | ```text:~/.zshrc 15 | PS1='%B%F{red}%(?^^%? )%f%b%n@%M:%~ %# ' 16 | bindkey -v 17 | alias ls='ls --color=auto' 18 | ``` 19 | ## 確認 20 | ``` 21 | $ echo $SHELL 22 | ``` 23 | # timedatectl の有効化 24 | ``` 25 | # timedatectl set-ntp true 26 | ``` 27 | ## 確認 28 | ``` 29 | $ timedatectl status 30 | ``` 31 | # git のインストールと設定 32 | ``` 33 | $ sudo pacman -S git 34 | $ git config --global user.name (名前) 35 | $ git config --global user.email (メールアドレス) 36 | ``` 37 | ## 確認 38 | ``` 39 | $ git --version 40 | ``` 41 | # yay のインストール 42 | 上の git を使います. 43 | ``` 44 | $ git clone https://aur.archlinux.org/yay.git 45 | $ cd yay 46 | $ makepkg -si 47 | ``` 48 | ## 確認 49 | ``` 50 | $ yay --version 51 | ``` 52 | # man のインストール 53 | ArchWiki には「base メタパッケージの依存関係としてインストールされています」なんて書いてありますが,消えました. 54 | ``` 55 | $ yay -S man-db man-pages 56 | ``` 57 | ## 確認 58 | ``` 59 | $ man man 60 | ``` 61 | # X のインストール 62 | startx を使って X を起動する人向けです. 63 | ``` 64 | $ yay -S xorg-server xorg-apps xorg-xinit 65 | $ yay -S i3 rofi terminator 66 | $ yay -S noto-fonts noto-fonts-cjk terminus-font-ttf ttf-liberation 67 | $ cp /etc/X11/xinit/xinitrc .xinitrc 68 | ``` 69 | `~/.xinitrc` を編集して最後を次のように書き換えます: 70 | ```text:~/.xinitrc 71 | ... 72 | case $2 in 73 | "" | "i3" ) exec i3 ;; 74 | "exec" ) exec $3 ;; 75 | * ) ;; 76 | esac 77 | ``` 78 | `$ startx i3` と打つか単に `$ startx` と打ったときは `i3` が起動しますが, `$ start exec ` と打つと `i3` の代わりにコマンドが実行されます. 79 | # X の設定 80 | 注意:Programmer Dvorak なのでこうしています.それ以外であれば XKbLayout と XKbVariant を変える必要があります. 81 | ```text:/etc/X11/xorg.conf.d/00-keyboard.conf 82 | Section "InputClass" 83 | Identifier "system-keyboard" 84 | MatchIsKeyboard "on" 85 | Option "XKbLayout" "us" 86 | Option "XKbVariant" "dvp" 87 | EndSection 88 | ``` 89 | DPMS によってスクリーンセーバが起動するのを防ぎます. 90 | ```text:/etc/X11/xorg.conf.d/10-monitor.conf 91 | Section "ServerLayout" 92 | Identifier "ServerLayout0" 93 | Option "StandbyTime" "0" 94 | Option "SuspendTime" "0" 95 | Option "OffTime" "0" 96 | Option "BlankTime" "0" 97 | EndSection 98 | ``` 99 | `.xinitrc` の `exec` より前のどこかに次を追記します: 100 | ```text:~/.xinitrc 101 | xset s off 102 | ``` 103 | # i3 の設定 104 | 注意:これはあくまで私が使っている config です. i3 の config は自動で作られるものを使っても良いと思います. 105 | ``` 106 | $ git clone https://github.com/fiveseven-lambda/i3configmaker.git 107 | $ cd i3configmaker 108 | $ make 109 | ``` 110 | # GraphicsMagick のインストール 111 | 上の i3 の設定の中で,スクリーンショットを撮るためのツールとして使われています. 112 | ``` 113 | $ yay -S graphicsmagick 114 | ``` 115 | ## 確認 116 | ``` 117 | $ gm -version 118 | ``` 119 | # fcitx-mozc のインストール 120 | ``` 121 | $ yay -S fcitx-mozc fcitx-im fcitx-configtool 122 | ``` 123 | ```text:~/.xinitrc 124 | ... 125 | export GTK_IM_MODULE=fcitx 126 | export QT_IM_MODULE=fcitx 127 | export XMODIFIERS=@im=fcitx 128 | 129 | case $2 in 130 | "" | "fcitx" ) fcitx-autostart & exec i3 ;; 131 | "i3" ) exec i3 ;; 132 | "exec" ) exec $3 ;; 133 | * ) ;; 134 | esac 135 | ``` 136 | 単に `$ startx` と打ったときは `fcitx` が起動し, `$ startx i3` と打ったときは `fcitx` が起動しないようにしています. 137 | # pulseaudio のインストールと起動 138 | ``` 139 | $ yay -S pulseaudio pavucontrol 140 | $ pulseaudio --start 141 | ``` 142 | ## 確認 143 | ``` 144 | $ pavucontrol 145 | ``` 146 | # unzip-iconv のインストール 147 | デフォルトで入っている unzip は,文字コードを指定するための -O オプションが使えません.そこで代わりに 148 | ``` 149 | $ yay -S unzip-iconv 150 | ``` 151 | で unzip-iconv をインストールします.途中で 152 | ``` 153 | :: unzip-iconv and unzip are in conflict. Remove unzip? [y/N] 154 | ``` 155 | と訊かれたら `y` と答えます. 156 | -------------------------------------------------------------------------------- /articles/9d566e93edfb4f95bb50.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "SATySFi で \\widehat を実装する" 3 | emoji: "📄" 4 | type: "tech" 5 | topics: ["satysfi"] 6 | published: true 7 | --- 8 | これは [SATySFi Advent Calender 2020](https://adventar.org/calendars/4965) の 9 日目の記事です. 8 日目は amutake さんによる「[SATySFi で書かれた本などのまとめを作りました](https://amutake.hatenablog.com/entry/2020/12/08/083925)」でした. 10 日目は monaqa さんによる「[【入門記事】SATySFi のコマンドの文法](https://zenn.dev/monaqa/articles/2020-12-10-satysfi-for-beginner-command-syntax)」です. 9 | 10 | # \widehat 11 | 今回は, SATySFi の数式コマンドとして `\widehat` を実装したいと思います. 12 | 13 | TeX の `\widehat` と同じように,数式内で 14 | ``` 15 | ${ \widehat{x}, \widehat{x^2}, \widehat{xy}, \widehat{xyz} } 16 | ``` 17 | の形で使えるようにします.見た目は次のようになります:![](https://storage.googleapis.com/zenn-user-upload/pqb359emjp2olf4lyf1camzptbyu) 18 | 19 | 型は次のようになります. 20 | `\widehat`: `math` → `math` 21 | # 使用するプリミティブ 22 | 今回使用するプリミティブを順に説明していきます. 23 | ## inline-graphics 24 | `inline-graphics`: `length` → `length` → `length` → (`point` → `graphics list`) → `inline-boxes` 25 | 26 | グラフィックは `graphics` 型によって表されるのですが,実際にこれを `inline-boxes` として本文中に組み込むためには,まず横幅/高さ/深さを教えることで SATySFi に組版して文章中の位置を決めてもらい,そうして決定された左下の座標を基準として `graphics` を作る必要があります.これを実現するのが `inline-graphics` プリミティブです. 27 | 28 | 第 1, 2, 3 引数にそれぞれ横幅,高さ,深さを渡し,第 4 引数には「座標を受け取って `graphics` を返す関数」を渡します. 29 | 30 | これが `inline-boxes` となって数式中に入るので, `graphics list` には `\widehat` 自体に加えて, `\widehat` を付ける対象となる数式も含める必要があります. 31 | ## embed-math 32 | `embed-math` : `context` → `math` → `inline-boxes` 33 | 34 | `\widehat` を付ける対象となる数式を,まずこれで `inline-boxes` に変換します.すると, `get-natural-metrics` によって横幅/高さ/深さの情報が得られるようになり,また `draw-text` によって `graphics` に変換することができるようになります. 35 | ## get-natural-metrics 36 | `get-natural-metrics`: `inline-boxes` → `length` * `length` * `length` 37 | 38 | `inline-boxes` の横幅/高さ/深さを得ます.これをもとに, `\widehat` の位置や大きさと, `inline-graphics` に渡す最終的な横幅/高さ/深さを決めます. 39 | ## draw-text 40 | `draw-text`: `point` → `inline-boxes` → `graphics` 41 | 42 | 左下の座標と `inline-boxes` を渡すと,そこに `inline-boxes` の内容を描いた結果の `graphics` を返します. 43 | ## start-path, line-to, close-with-line 44 | `start-path`: `point` → `pre-path` 45 | `line-to`: `point` → `pre-path` → `pre-path` 46 | `close-with-line`: `pre-path` → `path` 47 | 48 | `graphics` を描くためには,まず `path` を作ります. `|>` 演算子を用いてメソッドチェーンのように書くと例えば次のようになります: 49 | 50 | ``` 51 | % 左下の座標が (x, y) で一辺の長さが 1pt の正方形を表す path 52 | start-path (x, y) 53 | |> line-to (x, y +' 1pt) 54 | |> line-to (x +' 1pt, y +' 1pt) 55 | |> line-to (x +' 1pt, y) 56 | |> close-with-line 57 | ``` 58 | ## fill 59 | `fill`: `color` → `path` → `graphics` 60 | 61 | `path` の内側を `color` で塗った `graphics` を返します. 62 | ## text-in-math 63 | `text-in-math`: `math-class` → (`context` → `inline-boxes`) → `math` 64 | 65 | 完成した `inline-boxes` を,数式中に組み込みます. `\widehat` の付いた数式の `math-class` は`MathOrd` で良いでしょう. 66 | # 設計 67 | `\widehat` は,真ん中の辺りで厚く,端の辺りで薄くなってほしいです.それぞれの厚さを `hat-thickness-mid` , `hat-thickness-end` とします. 68 | 69 | `\widehat` 自体の高さ `hat-height` は,横幅に比例させます. 70 | 71 | また, `\widehat` は,数式より少し高い場所に置かれるので,その間隔を `hat-pos` とします.数式の高さより `hat-pos` だけ上の位置から `\widehat` を書き始める感じです. 72 | 73 | それぞれの具体的な値は次のようにしてみました. 74 | | 変数 | 値 | 75 | |--|--| 76 | | `hat-thickness-mid` | .6pt | 77 | | `hat-thickness-end` | .2pt | 78 | | `hat-height` | 横幅の .2 倍 | 79 | | `hat-pos` | 1.2pt | 80 | # できたもの 81 | 最終的にこんな感じになりました: 82 | ``` 83 | let-math \widehat it = 84 | text-in-math 85 | MathOrd 86 | (fun ctx -> ( 87 | let it = embed-math ctx ${#it} in 88 | let (length, height, depth) = get-natural-metrics it in 89 | let hat-thickness-mid = .6pt in 90 | let hat-thickness-end = .2pt in 91 | let hat-height = length *' .2 in 92 | let hat-pos = 1.2pt in 93 | inline-graphics 94 | length 95 | (height +' hat-pos +' hat-height +' hat-thickness-mid) 96 | depth 97 | (fun (x, y) -> [ 98 | draw-text (x, y) it; 99 | fill 100 | Color.black 101 | ( 102 | start-path (x, y +' height +' hat-pos) 103 | |> line-to (x, y +' height +' hat-pos +' hat-thickness-end) 104 | |> line-to (x +' length *' 0.5, y +' height +' hat-pos +' hat-height +' hat-thickness-mid) 105 | |> line-to (x +' length, y +' height +' hat-pos +' hat-thickness-end) 106 | |> line-to (x +' length, y +' height +' hat-pos) 107 | |> line-to (x +' length *' 0.5, y +' height +' hat-pos +' hat-height) 108 | |> close-with-line 109 | ) 110 | ]) 111 | )) 112 | ``` 113 | 良ければ使ってみてください! 114 | -------------------------------------------------------------------------------- /articles/competitive-programming-modinv.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ACL の inv gcd の正体" 3 | emoji: "⛳" 4 | type: "tech" 5 | topics: [競技プログラミング] 6 | published: true 7 | --- 8 | # inv gcd 9 | AtCoder Library の中の `internal_math.hpp` というファイルを開くと, `inv_gcd` という名前の関数があります. 2 つの整数 $a$ , $b$ を受け取って, 2 数の最大公約数 $g = \mathrm{gcd}(a, b)$ と, $xa \equiv g \mod b$ なる $x$ (ただし $0 \leq x < \frac bg$)を返します. 10 | 11 | この記事では,この関数がどのように実装されているか考えます. 12 | 13 | # 手計算 14 | まず, $a = 100$ , $b = 529$ とした場合を手計算してみます.一般的に知られているやり方は, 15 | 16 | $$ 17 | \begin{alignedat}{5} 18 | 529 &\div &100 = 5 &\cdots &29 \\ 19 | 100 &\div &29 = 3 &\cdots &13 \\ 20 | 29 &\div &13 = 2 &\cdots &3 \\ 21 | 13 &\div &3 = 4 &\cdots &1 \\ 22 | \end{alignedat} 23 | $$ 24 | 25 | を変形して 26 | 27 | $$ 28 | \begin{alignedat}{3} 29 | 29 &= &529 &- 5 \times &100 \\ 30 | 13 &= &100 &- 3 \times &29 \\ 31 | 3 &= &29 &- 2 \times &13 \\ 32 | 1 &= &13 &- 4 \times &3 \\ 33 | \end{alignedat} 34 | $$ 35 | 36 | とした上で, 37 | 38 | $$ 39 | \begin{aligned} 40 | 1 &= 1 \times 13 &&+ (-4) \times 3 \\ 41 | &= 1 \times 13 &&+ (-4) \times (29 - 2 \times 13) \\ 42 | &= (-4) \times 29 &&+ (1 - (-4) \times 2) \times 13 \\ 43 | &= (-4) \times 29 &&+ 9 \times 13 \\ 44 | &= (-4) \times 29 &&+ 9 \times (100 - 3 \times 29) \\ 45 | &= 9 \times 100 &&+ (-4 - 9 \times 3) \times 29 \\ 46 | &= 9 \times 100 &&+ (-31) \times 29 \\ 47 | &= 9 \times 100 &&+ (-31) \times (529 - 5 \times 100) \\ 48 | &= (-31) \times 529 &&+ (9 - (-31) \times 5) \times 100 \\ 49 | &= (-31) \times 529 &&+ 164 \times 100 50 | \end{aligned} 51 | $$ 52 | 53 | から $164 \times 100 \equiv 1 \mod 529$ を得るものでしょう.これは, 54 | 55 | $$ 56 | \begin{aligned} 57 | 1 &= x_1 \times 13 + x_2 \times 3 \\ 58 | &= x_2 \times 29 + x_3 \times 13 \\ 59 | &= x_3 \times 100 + x_4 \times 29 \\ 60 | &= x_4 \times 529 + x_5 \times 100 61 | \end{aligned} 62 | $$ 63 | 64 | としたときに 65 | 66 | $$ 67 | \begin{aligned} 68 | x_1 &= 1 \\ 69 | x_2 &= -4 \\ 70 | x_3 &= x_1 - x_2 \times 2 \\ 71 | x_4 &= x_2 - x_3 \times 3 \\ 72 | x_5 &= x_3 - x_4 \times 5 \\ 73 | \end{aligned} 74 | $$ 75 | 76 | という計算で $x_5$ が求められるということを意味します.さらに $x_0 = 0$ とすると 77 | 78 | $$ 79 | \begin{aligned} 80 | x_0 &= 0 \\ 81 | x_1 &= 1 \\ 82 | x_2 &= x_0 - x_1 \times 4 \\ 83 | x_3 &= x_1 - x_2 \times 2 \\ 84 | x_4 &= x_2 - x_3 \times 3 \\ 85 | x_5 &= x_3 - x_4 \times 5 \\ 86 | \end{aligned} 87 | $$ 88 | 89 | となります. 90 | 91 | # 一般化 92 | 以上の議論を全て文字に置き換えると, $a_0 = b$ , $a_1 = a$ として互除法を 93 | 94 | $$ 95 | \begin{aligned} 96 | a_0 \div a_1 &= q_0 \cdots a_2 \\ 97 | a_1 \div a_2 &= q_1 \cdots a_3 \\ 98 | a_2 \div a_3 &= q_2 \cdots a_4 \\ 99 | &\vdots \\ 100 | a_{n - 1} \div a_n &= q_{n - 1} \cdots 0 \\ 101 | \end{aligned} 102 | $$ 103 | 104 | とした上で, 105 | 106 | $$ 107 | \begin{aligned} 108 | x_0 &= 0 \\ 109 | x_1 &= 1 \\ 110 | x_2 &= x_0 - x_1 \times q_{n - 2} \\ 111 | x_3 &= x_1 - x_2 \times q_{n - 3} \\ 112 | &\vdots \\ 113 | x_n &= x_{n - 2} - x_{n - 1} \times q_0 \\ 114 | \end{aligned} 115 | $$ 116 | 117 | としたときの $x_n$ が求めたかったものでした. 118 | 119 | しかし,互除法では $q_0, q_1, q_2, \ldots$ の順番に求まるのに対し, $x_0, x_1, x_2, \ldots$ を求めるときは $q_{n - 2}, q_{n - 3}, q_{n - 4}, \ldots$ の順番で使うので,このままだと各 $q_i$ の値を保存しておかなければなりません.これを避けるため,計算順序を変えることを考えます. 120 | 121 | # 行列表記 122 | $$ 123 | x_i = x_{i - 2} - x_{i - 1} \times q_{n - i} 124 | $$ 125 | 126 | は,行列を用いると 127 | 128 | $$ 129 | \begin{pmatrix} 130 | x_{i - 1} \\ 131 | x_{i} 132 | \end{pmatrix} 133 | = 134 | \begin{pmatrix} 135 | 0 & 1 \\ 136 | 1 & -q_{n - i} 137 | \end{pmatrix} 138 | \begin{pmatrix} 139 | x_{i - 2} \\ 140 | x_{i - 1} 141 | \end{pmatrix} 142 | $$ 143 | 144 | と書くことができます.よって, $x_n$ を求める段階は 145 | 146 | $$ 147 | \begin{aligned} 148 | \begin{pmatrix} 149 | x_{n - 1} \\ 150 | x_n 151 | \end{pmatrix} 152 | &= 153 | \begin{pmatrix} 154 | 0 & 1 \\ 155 | 1 & -q_0 156 | \end{pmatrix} 157 | \begin{pmatrix} 158 | x_{n - 2} \\ 159 | x_{n - 1} 160 | \end{pmatrix} \\ 161 | \begin{pmatrix} 162 | x_{n - 2} \\ 163 | x_{n - 1} 164 | \end{pmatrix} 165 | &= 166 | \begin{pmatrix} 167 | 0 & 1 \\ 168 | 1 & -q_1 169 | \end{pmatrix} 170 | \begin{pmatrix} 171 | x_{n - 3} \\ 172 | x_{n - 2} 173 | \end{pmatrix} \\ 174 | \vdots \\ 175 | \begin{pmatrix} 176 | x_{2} \\ 177 | x_{3} 178 | \end{pmatrix} 179 | &= 180 | \begin{pmatrix} 181 | 0 & 1 \\ 182 | 1 & -q_{n - 3} 183 | \end{pmatrix} 184 | \begin{pmatrix} 185 | x_1 \\ 186 | x_2 187 | \end{pmatrix} \\ 188 | \begin{pmatrix} 189 | x_{1} \\ 190 | x_{2} 191 | \end{pmatrix} 192 | &= 193 | \begin{pmatrix} 194 | 0 & 1 \\ 195 | 1 & -q_{n - 2} 196 | \end{pmatrix} 197 | \begin{pmatrix} 198 | x_0 \\ 199 | x_1 200 | \end{pmatrix} \\ 201 | &= 202 | \begin{pmatrix} 203 | 0 & 1 \\ 204 | 1 & -q_{n - 2} 205 | \end{pmatrix} 206 | \begin{pmatrix} 207 | 0 \\ 208 | 1 209 | \end{pmatrix} 210 | \end{aligned} 211 | $$ 212 | と書き換えることができます.これを一本にすると 213 | 214 | $$ 215 | \begin{pmatrix} 216 | x_{n - 1} \\ 217 | x_n \\ 218 | \end{pmatrix} 219 | = 220 | \begin{pmatrix} 221 | 0 & 1 \\ 222 | 1 & -q_0 223 | \end{pmatrix} 224 | \begin{pmatrix} 225 | 0 & 1 \\ 226 | 1 & -q_1 227 | \end{pmatrix} 228 | \cdots 229 | \begin{pmatrix} 230 | 0 & 1 \\ 231 | 1 & -q_{n - 2} 232 | \end{pmatrix} 233 | \begin{pmatrix} 234 | 0 \\ 235 | 1 \\ 236 | \end{pmatrix} 237 | $$ 238 | 239 | となり, $q_0, q_1, q_2, \ldots$ の順に使って計算することができるようになりました. 240 | 241 | # 変形 242 | 行列を左から順に計算すると, 243 | 244 | $$ 245 | \begin{aligned} 246 | A_0 &= \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} \\ 247 | A_1 &= A_0 \begin{pmatrix} 0 & 1 \\ 1 & -q_0 \end{pmatrix} \\ 248 | A_2 &= A_1 \begin{pmatrix} 0 & 1 \\ 1 & -q_1 \end{pmatrix} \\ 249 | \vdots \\ 250 | A_{n - 1} &= A_{n - 2} \begin{pmatrix} 0 & 1 \\ 1 & -q_{n - 2} \end{pmatrix} \\ 251 | \begin{pmatrix} x_{n - 1} \\ x_n \end{pmatrix} &= A_{n - 1} \begin{pmatrix} 0 \\ 1 \end{pmatrix} \\ 252 | \end{aligned} 253 | $$ 254 | 255 | となります. 256 | 257 | 行列 $\begin{pmatrix} x & y \\ z & w \end{pmatrix}$ に行列 $\begin{pmatrix} 0 & 1 \\ 1 & -q_i \end{pmatrix}$ を右からかけると $\begin{pmatrix}y & x - q_iy \\ w & z - q_iw\end{pmatrix}$ となり,上の行( $x$ $y$ )と下の行( $z$ $w$ )は独立しています.よって,下の行のみに着目することで次のように書き換えることができます. 258 | 259 | $$ 260 | \begin{aligned} 261 | (z_0,\; w_0) &= (0,\; 1) \\ 262 | (z_1,\; w_1) &= (w_0,\; z_0 - q_0w_0) \\ 263 | (z_2,\; w_2) &= (w_1,\; z_1 - q_1w_1) \\ 264 | \vdots \\ 265 | (z_{n - 1},\; w_{n - 1}) &= (w_{n - 2},\; z_{n - 2} - q_{n - 2}w_{n - 2}) \\ 266 | x_n &= w_{n - 1} 267 | \end{aligned} 268 | $$ 269 | 270 | こうして,互除法を行いながら $(z,\;w)$ を更新することで $x_n$ が得られました. 271 | 272 | # 確認 273 | 最初の $a = 100$ , $b = 529$ の場合について計算して確かめてみます. 274 | 275 | 1. $(z_0,\; w_0) = (0,\; 1)$ 276 | 1. $529 \div 100$ の商は $5$ なので $(z_1,\; w_1) = (w_0,\; z_0 - 5w_0) = (1,\; -5)$ 277 | 1. $100 \div 29$ の商は $3$ なので $(z_2,\; w_2) = (w_1,\; z_1 - 3w_1) = (-5,\; 16)$ 278 | 1. $29 \div 13$ の商は $2$ なので $(z_3,\; w_3) = (w_2,\; z_2 - 2w_2) = (16,\; -37)$ 279 | 1. $13 \div 3$ の商は $4$ なので $(z_4,\; w_4) = (w_3,\; z_3 - 4w_3) = (-37,\; 164)$ 280 | 281 | ちゃんと,最後に $w_4$ の値が $164$ になっています. 282 | -------------------------------------------------------------------------------- /articles/conky-with-i3bar.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "i3status の代替としての conky" 3 | emoji: "📝" 4 | type: "tech" 5 | topics: ["i3wm"] 6 | published: true 7 | --- 8 | 9 | i3 window manager のデフォルトの設定では,画面下に表示されるバー (i3bar) の内容は i3status によって生成されています.ためしにコンソールで `$ i3status` を実行すると,バーと同じ内容が出てくるでしょう. 10 | 11 | i3status の生成する文字列は `~/.config/i3status/config` で設定できますが,i3status 自体を別のもので置き換えてさらに自由度の高い設定をすることも可能です.ここで代替として使えるのが conky です. 12 | 13 | conky はもともと GUI 用のシステム監視ソフトで,CPU や RAM の使用状況などをウィンドウに表示してくれるのですが,GUI ではなく代わりに stdout に出力させるようにも設定できます.これを i3bar に渡してやれば,conky が i3status の代替となるのです. 14 | 15 | [英語の解説ページ](https://i3wm.org/docs/user-contributed/conky-i3bar.html). 16 | 17 | # conky のインストール 18 | [conky](https://www.archlinux.jp/packages/extra/x86_64/conky/) パッケージがあります. 19 | ``` 20 | $ pacman -S conky 21 | ``` 22 | 設定ファイルは Lua の文法で書き,`~/.config/conky/conky.conf` に置きます.`$ conky -C` で雛形が生成できます. 23 | ``` 24 | $ mkdir -p ~/.config/conky 25 | $ conky -C > ~/.config/conky/conky.conf 26 | ``` 27 | `conky.conf` を開いてみると,`conky.config =` の後にたくさん設定項目があります.`out_to_X` と`own_window` を `false` にして,`out_to_console` を `true` にすれば,`conky` を実行してもウィンドウが表示されず,代わりにシステム情報が stdout へ出力されるようになります. 28 | 29 | `conky.text =` の後の 2 重四角括弧 `[[` 〜 `]]` の中で出力の内容を設定します(Lua の改行可能な文字列リテラルらしい). 30 | 31 | # conky - i3bar 間の橋渡し 32 | `i3bar` は,バーに表示する内容を以下のような JSON で受け取ります. 33 | 34 | - 全体は 1 つの配列 A. 35 | - A の各要素 A[t] は,時刻 t におけるバー全体の内容. 36 | - A[t] も配列. 37 | - A[t] の各要素 A[t][i] は,バーを構成するブロック(CPU 使用率のブロックとか,RAM 使用率のブロックとか). 38 | - A[t][i] はオブジェクト. 39 | - A[t][i]['full_text'] でテキスト,A[t][i]['color'] で文字色を指定する. 40 | 41 | `conky` が A の各要素 A[t] を一定の時間間隔(たとえば 1 秒おき)で `i3bar` に入力すると,そのたびにバーの表示内容が更新されます. 42 | 43 | `conky` は毎回同じ内容を出力します.一方 JSON の配列は `[` で始まってその後カンマ区切りで要素が続くので,毎回同じ文字列だと最初の `[` だけ出力できません.そこで,軽くスクリプトをかませる必要があります. 44 | 45 | 以下のスクリプトをどこかに置いておき,`conky` を直接呼び出す代わりにこのスクリプトを呼び出すことにします. 46 | ```bash 47 | #!/bin/bash 48 | echo '{"version":1}' 49 | echo '[' 50 | exec conky 51 | ``` 52 | `{"version":1}` もなんかヘッダとして要るっぽいです. 53 | 54 | そうしたら,`~/.config/i3/config` も編集します. 55 | ``` 56 | bar { 57 | status_command (スクリプトの場所) 58 | } 59 | ``` 60 | # 設定 61 | さて,あとは `~/.config/conky/conky.conf` 内の `conky.text =` 以降を編集するだけです! 62 | 63 | conky(1) を見ながらがいいです.あと,こういうとき JSON が trailing comma ダメなの腹たつ. 64 | 65 | 手始めに,RAM 使用率,CPU 使用率,時刻だけを表示する次のような例を見てみます. 66 | ```lua 67 | conky.text = [[ 68 | [ 69 | {"full_text": "RAM $mem / $memmax ($memperc%)"}, 70 | {"full_text": "CPU $cpu%"}, 71 | {"full_text": "$time"} 72 | ], 73 | ]] 74 | ``` 75 | まず,`[[`〜`]]` で囲まれた部分が Lua の文字列です(このとき `"` は関係ありません). 76 | 77 | よって,`conky.text` には以下のような文字列が代入されることになります. 78 | ``` 79 | [ 80 | {"full_text": "RAM $mem / $memmax ($memperc%)"}, 81 | {"full_text": "CPU $cpu%"}, 82 | {"full_text": "$time"} 83 | ], 84 | ``` 85 | 次に,conky がこの文字列中の `$` で始まる部分を値で置き換えて出力します(ここでも `"` は関係ありません).`$mem` はメモリ使用量,`$cpu` は CPU 使用率,`$time` は現在時刻といった具合です.これにより,conky の出力はたとえば 86 | ```json 87 | [ 88 | {"full_text": "RAM 4.00 GiB / 16.0 GiB (25%)"}, 89 | {"full_text": "CPU 10%"}, 90 | {"full_text": "2022-02-08 20:00:00"} 91 | ], 92 | ``` 93 | のようになります. 94 | 95 | i3bar はこれを JSON として読み取って 96 | ``` 97 | RAM 4.00 GiB / 16.0 GiB (25%)|CPU 10%|2022-02-08 20:00:00 98 | ``` 99 | のように表示します. 100 | 101 | `${cpu cpu1}` のように `{`〜`}` でくくると引数を与えられます.この例だと `$cpu` に `cpu1` という引数を渡していて,これは 1 つめのコアの使用率を表します(1 から数える.`cpu0` は総使用率). 102 | 103 | Lua,conky,JSON という 3 段階を経ているのでややこしいですね. 104 | 105 | # 文字色を変える 106 | i3bar に渡す JSON では, 107 | ```json 108 | {"full_text": "CPU 10%", "color": "#ff0000"} 109 | ``` 110 | のように `color` プロパティを追加することで文字色が指定できます. 111 | 112 | `$if_match` を使うと,CPU 使用率が 90% 未満なら白,90% 以上なら赤というように,条件分岐を挟むことができます. 113 | ```lua 114 | conky.text = [[ [ 115 | { 116 | "full_text": "CPU $cpu%", 117 | "color": "${if_match $cpu < 90}\#ffffff${else}\#ff0000${endif}" 118 | } 119 | ], ]] 120 | ``` 121 | これにより,CPU 使用率が 10% のときと 95% のときの conky の出力はそれぞれ 122 | ```json 123 | [ 124 | { 125 | "full_text": "CPU 10%", 126 | "color": "#ffffff" 127 | } 128 | ], 129 | ``` 130 | ```json 131 | [ 132 | { 133 | "full_text": "CPU 95%", 134 | "color": "#ff0000" 135 | } 136 | ], 137 | ``` 138 | となるわけです. 139 | 140 | # Lua の関数を呼び出す 141 | 文字色を 2 つの間で切り替えるだけでなく,CPU 使用率が増えるにつれて段々と赤に近づくようにするには,どうすれば良いでしょうか.こんなときは `$lua` を使って Lua の関数を呼び出します. 142 | 143 | まず適当な `.lua` ファイルを用意し,呼び出したい関数を書きます.このとき関数名は `conky_` で始まっている必要があります.そして `conky.config = {`〜`}` の中に `lua_load = '(ファイル名).lua'` を追加します. 144 | 145 | この状態で `conky.text` の中に `${lua (関数名)}`(ただし関数名の先頭の `conky_` は取り除く)という文字列を含めておくと,conky は関数を呼び出し,その部分を返り値で置き換えます. 146 | 147 | また,`${lua (関数名) (引数)}` と書くと引数も渡せます.ただしこのとき,`${lua fnc $cpu}` と書いても,`conky_fnc('10')` のように置換後の `$cpu` が渡されることはなく,呼び出しは `conky_fnc('$cpu')` となります.関数内で実際の CPU 使用率を得るには,`conky_parse` 関数を使って `conky_parse('$cpu')` と書きます. 148 | 149 | さて,今回は文字色を変えるために以下のような関数を書きました. 150 | ```lua 151 | function conky_color(arg) 152 | tmp = math.floor(conky_parse(arg) * 255 / 100) 153 | return string.format("#%02x%02x%02x", tmp, 255 - tmp, 255 - tmp) 154 | end 155 | ``` 156 | これにより,`conky.text` 中にたとえば `${lua color $cpu}` と書くと,置換時に `conky_color('$cpu')` という関数呼び出しが起こり,その中で `conky_parse('$cpu')` が CPU 使用率を返すので,その値に応じて文字色を表す `#XXXXXX` 形式の文字列が返されます.よって,`conky.conf` 内には 157 | ```lua 158 | conky.text = [[ [ 159 | { 160 | "full_text": "RAM $mem / $memmax ($memperc%)", 161 | "color": "${lua color $memperc}" 162 | }, 163 | { 164 | "full_text": "CPU $cpu%", 165 | "color": "${lua color $cpu}" 166 | }, 167 | { 168 | "full_text": "$time" 169 | } 170 | ], ]] 171 | ``` 172 | のように書いておけば良いわけです. 173 | 174 | # Lua の機能を使う 175 | たとえば CPU が 8 コア 16 スレッドあって,それぞれの使用率を `${cpu cpu1}` から `${cpu cpu16}` まで並べて全部表示したいとします.こんなときは,Lua の機能が役立ちます. 176 | ```lua 177 | cpu = {} 178 | for i = 1, 16 do 179 | cpu[i] = 180 | '{"full_text": "${cpu cpu' .. i .. '}",' .. 181 | '"color": "${lua color ${cpu cpu' .. i .. '}}"}' 182 | end 183 | conky.text = '[' .. table.concat(cpu, ',') .. ']' 184 | ``` 185 | もはや何がなんだかって感じですね. 186 | 187 | `'${cpu cpu' .. i .. '}'` は,`'${cpu cpu'` と `i` と `'}'` を文字列として連結したものなので,たとえば `i` の値が 5 なら `${cpu cpu5}` となります. 188 | 189 | `table.concat(cpu, ',')` は,`cpu[1]` から `cpu[16]` までを `,` 区切りで連結します. 190 | 191 | 結果的に `conky.text` には cpu1 の表示から cpu16 の表示までが JSON で突っ込まれることになります. 192 | 193 | 最後 `conky.text` に代入している部分では,前後に他のブロックも追加できます. 194 | 195 | これでだいぶ自由度の高い設定ができるようになるんじゃないかと思います. 196 | 197 | # おまけ 198 | 最後に,私が今回個人的に作って良かったなと思ったやつを紹介します. 199 | 200 | 定期的にパッケージマネージャによる更新をかけて各々のパッケージを最新の状態に保つのは大事です.私は yay を使っているので定期的に `$ yay -Syu` あるいは単に `$ yay` を打つことで更新をしています. 201 | 202 | しかし,たまに数日間更新を忘れてしまうことがあります.別に何かが忙しかったわけではなく,普通に忘れちゃいます.こういうのは無くしたいなと前から思っていました. 203 | 204 | そこで,i3bar に「最後に `$ yay` を打ってから経った時間」を表示して,数日経つと色が赤く染まるようにすることを思いつきました. 205 | 206 | やり方は簡単です.まず,`yay` を間接的に呼び出しながら時刻を記録するスクリプトを用意しておいて, 207 | ```bash:~/.config/conky/yay 208 | #!/bin/zsh 209 | if [ $# -eq 0 ]; then 210 | date "+%Y-%m-%d %H:%M:%S" > ~/.config/conky/last-yay 211 | fi 212 | yay $@ 213 | ``` 214 | `yay` コマンドをこれで差し替えます. 215 | ```bash:~/.zshrc 216 | alias yay='~/.config/conky/yay' 217 | ``` 218 | あとは,記録された時刻と現在時刻の差をとって出力する処理を python で書いて 219 | ```python:~/.config/conky/time.py 220 | from datetime import datetime 221 | diff = datetime.now() - datetime.strptime(input(), '%Y-%m-%d %H:%M:%S') 222 | seconds = int(diff.total_seconds()) 223 | hours, minutes = divmod(seconds // 60, 60) 224 | r = min(255, seconds * 256 // (3 * 24 * 60 * 60)) 225 | gb = 255 - r 226 | print(f'{{"full_text": "{hours}:{minutes:02}", "color": "#{r:02x}{gb:02x}{gb:02x}"}}') 227 | ``` 228 | `conky.conf` 内で `conky.text` に追加すれば終わりです! 229 | ```lua:~/.config/conky/conky.conf 230 | conky.text = [[ [ 231 | ${exec python $HOME/.config/conky/time.py < $HOME/.config/conky/last-yay}, 232 | ]] .. -- 残りの内容 233 | ``` 234 | 235 | これでもう更新を忘れない! -------------------------------------------------------------------------------- /articles/control-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "言語処理系のツボ:制御フロー (WIP)" 3 | emoji: "🐈" 4 | type: "tech" 5 | topics: ["rust"] 6 | published: true 7 | --- 8 | :::message 9 | 未完成記事です 残しておかないと存在を忘れてしまう…… 10 | ::: 11 | 12 | 『コンパイラ 原理・技法・ツール(第 2 版)』の、制御フローに関する部分の基本をおさえた以下の短いコードについて、説明する記事を書きます(予定) 13 | ```rust 14 | fn main() { 15 | let input = " 16 | a b c 17 | if x 18 | d e 19 | else 20 | f g 21 | end 22 | h i j 23 | if y 24 | k l 25 | if z 26 | m 27 | else 28 | n o 29 | end 30 | p q 31 | end 32 | r s t 33 | while x 34 | u v 35 | end 36 | w 37 | end"; 38 | let mut words = input.split_whitespace(); 39 | let mut stmts = Vec::new(); 40 | parse(&mut words, &mut stmts, None); 41 | 42 | for stmt in &stmts { 43 | stmt.translate(); 44 | } 45 | println!("End."); 46 | } 47 | 48 | enum Stmt { 49 | Expr(String), 50 | If(String, Vec, Vec), 51 | While(String, Vec), 52 | Break, 53 | Continue, 54 | } 55 | 56 | fn parse<'a>( 57 | words: &mut impl Iterator, 58 | stmts: &mut Vec, 59 | stmts_else: Option<&mut Vec>, 60 | ) { 61 | loop { 62 | match words.next().unwrap() { 63 | "if" => stmts.push({ 64 | let cond = words.next().unwrap(); 65 | let mut stmts_then = Vec::new(); 66 | let mut stmts_else = Vec::new(); 67 | parse(words, &mut stmts_then, Some(&mut stmts_else)); 68 | Stmt::If(String::from(cond), stmts_then, stmts_else) 69 | }), 70 | "else" => return parse(words, stmts_else.unwrap(), None), 71 | "while" => stmts.push({ 72 | let cond = words.next().unwrap(); 73 | let mut stmts = Vec::new(); 74 | parse(words, &mut stmts, None); 75 | Stmt::While(String::from(cond), stmts) 76 | }), 77 | "break" => stmts.push(Stmt::Break), 78 | "continue" => stmts.push(Stmt::Continue), 79 | "end" => return, 80 | expr => stmts.push(Stmt::Expr(String::from(expr))), 81 | } 82 | } 83 | } 84 | 85 | impl Stmt { 86 | fn translate(&self) { 87 | match self { 88 | Stmt::Expr(expr) => print!("{expr}, "), 89 | Stmt::If(cond, stmts_then, stmts_else) => { 90 | println!("Br {cond}."); 91 | for stmt in stmts_then { 92 | stmt.translate(); 93 | } 94 | println!("Jmp."); 95 | for stmt in stmts_else { 96 | stmt.translate(); 97 | } 98 | println!("Jmp."); 99 | } 100 | Stmt::While(cond, stmts) => { 101 | println!("Jmp."); 102 | println!("Br {cond}."); 103 | for stmt in stmts { 104 | stmt.translate(); 105 | } 106 | println!("Jmp."); 107 | } 108 | Stmt::Break | Stmt::Continue => println!("Jmp."), 109 | } 110 | } 111 | } 112 | ``` 113 | -------------------------------------------------------------------------------- /articles/double-binary-search.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "double の二分探索はループ 64 回で十分すぎる" 3 | emoji: "🔪" 4 | type: "tech" 5 | topics: ['二分探索', '競プロ'] 6 | published: false 7 | --- 8 | 9 | この記事では,「非 NaN 値」という言葉を「実数,正の無限大,負の無限大のうち,倍精度浮動小数点数で表せる数」という意味で使います. 10 | 11 | # 序 12 | 倍精度浮動小数点数(C/C++ だと `double`,Rust だと `f64`)に対する以下のような二分探索を考えます. 13 | 14 | - `double` を受け取って `bool` を返す関数 `f` が与えられる. 15 | - ある非 NaN 値 `x` が存在して,任意の非 NaN 値 `y` について 16 | - `y <= x` なら `f(y)` は `true` 17 | - `y > x` なら `f(y)` は `false` 18 | 19 | であることが分かっている. 20 | - 上の `x`(`f(x)` が `true` となる最大の `x`)を求めたい. 21 | 22 | 競プロではこれをよく相対誤差 $10^{-6}$ 以下とかで求めますね.誤差が○○以下になるには何回くらいループを回せばいいんだろう?みたいな話にもなります.たとえば[こことか](https://rsk0315.hatenablog.com/entry/2020/04/29/155009). 23 | 24 | でもよく考えると,`double` って 64 ビットです.ということは表せる数は多くても $2^{64}$ 通り(実際は NaN とか非正規化数のぶんもっと少ないです)で,二分探索だとこれが毎回のループで半分ずつ減っていくので,64 回あれば `double` そのものの限界まで誤差が減らせるはずですね. 25 | 26 | # じゃあなんで普通にやるとできないの 27 | 普通は,下限 $l$ と上限 $r$ があったら $m = (l + r) / 2$ で分割しますね.でも,たとえば $1$ 以上 $32$ 未満の範囲に `double` 値は $5 \times 2^{52}$ 通りありますが,そのうち $80\%$ の $4 \times 2^{52}$ 個が $1$ 以上 $16$ 未満の範囲にあります.つまり,半分ずつに分けていくのが二分探索だったのに,$[1, 32)$ を $[1, 16)$ と $[16, 32)$ に分けても,その中にある `double` 値の数で見ると半分になっていなかったわけですね. 28 | 29 | # どうしよう 30 | 符号なし 64 ビット整数(C/C++ だと `std::uint64_t`,Rust だと `u64`.競プロ C/C++ では `unsigned long long` も)を使います. 31 | 32 | `double` と `std::uint64_t` の間で,以下のような変換を行います. 33 | ```cpp:C++ 34 | std::uint64_t f2u(double x){ 35 | std::uint64_t b; 36 | std::memcpy(&b, &x, 8); 37 | return b >> 63 ? ~b : b ^ 1ull << 63; 38 | } 39 | 40 | double u2f(std::uint64_t b){ 41 | b = b >> 63 ? b ^ 1ull << 63 : ~b; 42 | double ret; 43 | std::memcpy(&ret, &b, 8); 44 | return ret; 45 | } 46 | ``` 47 | ```rust:Rust 48 | fn f2u(f: f64) -> u64 { 49 | let b = f.to_bits(); 50 | if b >> 63 == 1 { !b } else { b ^ 1 << 63 } 51 | } 52 | 53 | fn u2f(b: u64) -> f64 { 54 | f64::from_bits(if b >> 63 == 1 { b ^ 1 << 63 } else { !b }) 55 | } 56 | ``` 57 | この変換 `f2u`,`u2f` は以下のような性質を満たします. 58 | 59 | - 逆変換になっている:任意の非 NaN 値 `x` について,`u2f(f2u(x))` は `x` に等しい. 60 | - 順序を保つ:任意の非 NaN 値 `x`,`y` について,`x <= y` ならば `f2u(x) <= f2u(y)`. 61 | - 間に非 NaN 値以外を挟まない:`x`,`y` が非 NaN 値のとき,`f2u(x)` 以上 `f2u(y)` 以下の任意の整数 `b` に対し `u2f(b)` は非 NaN 値. 62 | 63 | この性質のおかげで,`double` の二分探索は 64 ビット整数の二分探索へと成り下がります.あとは普通の二分探索に突っ込めば,多くても 64 回で `double` の限界までいける二分探索の完成です. 64 | ```cpp 65 | // 普通の(整数に対する)二分探索 66 | template 67 | std::uint64_t binary_search(F f, std::uint64_t l, std::uint64_t r){ 68 | while(l + 1 < r){ 69 | std::uint64_t mid = l + (r - l) / 2; 70 | (f(mid) ? l : r) = mid; 71 | } 72 | return l; 73 | } 74 | 75 | // double 二分探索 76 | template 77 | double binary_search_double(F f, double l = -INFINITY, double r = INFINITY){ 78 | return i2f( 79 | binary_search( 80 | [&](std::uint64_t b){ return f(i2f(b)); }, 81 | f2i(l), f2i(r) 82 | ) 83 | ); 84 | } 85 | ``` -------------------------------------------------------------------------------- /articles/equilibrium-simulation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "二分探索を用いた酸塩基平衡シミュレーション" 3 | emoji: "🧪" 4 | type: "tech" 5 | topics: ["シミュレーション", "化学"] 6 | published: true 7 | --- 8 | 9 | # 目標 10 | この記事では,酸塩基平衡のシミュレータを作るためのアルゴリズムについて紹介します. 11 | 12 | たとえば,以下の問題を解くことを考えます. 13 | 14 | ## 問題 15 | 酢酸 $\rm CH_3COOH$ は以下のように電離する. 16 | 17 | $$ 18 | \rm CH_3COOH \rightleftharpoons CH_3COO^- + H^+ 19 | $$ 20 | 21 | 電離定数は $10^{-4.76}$ である.すなわち 22 | 23 | $$ 24 | \rm \frac{[CH_3COO^-][H^+]}{[CH_3COOH]} = 10^{-4.76} \\ 25 | $$ 26 | 27 | リン酸 $\rm H_3PO_4$ は以下のように電離する. 28 | 29 | $$ 30 | \begin{aligned} 31 | \rm H_3PO_4 &\rightleftharpoons \rm H_2PO_4^- + H^+ \\ 32 | \rm H_2PO_4^- &\rightleftharpoons \rm HPO_4^{2-} + H^+ \\ 33 | \rm HPO_4^{2-} &\rightleftharpoons \rm PO_4^{3-} + H^+ 34 | \end{aligned} 35 | $$ 36 | 37 | 電離定数は順に $10^{-2.12}$,$10^{-7.21}$,$10^{-12.67}$ である.すなわち 38 | 39 | $$ 40 | \begin{aligned} 41 | \rm \frac{[H_2PO_4^-][H^+]}{[H_3PO_4]} &= 10^{-2.12} \\ 42 | \rm \frac{[HPO_4^{2-}][H^+]}{[H_2PO_4^-]} &= 10^{-7.21} \\ 43 | \rm \frac{[PO_4^{3-}][H^+]}{[HPO_4^{2-}]} &= 10^{-12.67} \\ 44 | \end{aligned} 45 | $$ 46 | 47 | 今,ビーカーに $1 [{\rm L}]$ の水を入れ,そこに酢酸を $c_1 [{\rm mol}]$,リン酸を $c_2 [{\rm mol}]$ 溶かした.この水溶液の $\rm pH$ を求めなさい. 48 | 49 | ## 一般化 50 | この問題は以下のように一般化されます. 51 | 52 | $m$ 種類の酸についての情報が与えられる. $i$ 番目($1 \le i \le m$)は $n_i$ 価の酸 ${\rm A}_i {\rm H}_{n_i}$ であり,以下のように電離する. 53 | 54 | $$ 55 | \begin{aligned} 56 | {\rm A}_i {\rm H}_{n_i} &\rightleftharpoons {\rm A}_i {\rm H}_{n_i - 1}^- + {\rm H}^+ \\ 57 | {\rm A}_i {\rm H}_{n_i - 1}^- &\rightleftharpoons {\rm A}_i {\rm H}_{n_i - 2}^{2-} + {\rm H}^+ \\ 58 | &\vdots \\ 59 | {\rm A}_i {\rm H}^{(n_i - 1)-} &\rightleftharpoons {\rm A}_i^{n_i-} + {\rm H}^+ 60 | \end{aligned} 61 | $$ 62 | 63 | $i$ 番目の酸の第 $j$ 解離定数は $K_{ij}$ である.すなわち, 64 | 65 | $$ 66 | \begin{aligned} 67 | \frac{[{\rm A}_i {\rm H}_{n_i - 1}^-][{\rm H}^+]}{[{\rm A}_i {\rm H}_{n_i}]} &= K_{i1} \\ 68 | \frac{[{\rm A}_i {\rm H}_{n_i - 2}^{2-}][{\rm H}^+]}{[{\rm A}_i {\rm H}_{n_i - 1}^-]} &= K_{i2} \\ 69 | &\vdots \\ 70 | \frac{[{\rm A}_i^{n_i-}][{\rm H}^+]}{[{\rm A}_i {\rm H}^{(n_i - 1)-}]} &= K_{in_i} 71 | \end{aligned} 72 | $$ 73 | 74 | 今,ビーカーに $1 [{\rm L}]$ の水を入れ,そこに酸 ${\rm A}_1 {\rm H}_{n_1}$ を $c_1 [{\rm mol}]$,酸 ${\rm A}_2 {\rm H}_{n_2}$ を $c_2 [{\rm mol}]$,……,酸 ${\rm A}_m {\rm H}_{n_m}$ を $c_m [{\rm mol}]$ 溶かした.この水溶液の $\rm pH$ を求めなさい. 75 | 76 | # 方針 77 | まず, $\rm H^+$ の濃度を決め打ちします.すると,電離平衡の式から各々の酸 ${\rm A}_i {\rm H}_{n_i}$ について 78 | 79 | $$ 80 | \begin{aligned} 81 | [{\rm A}_i {\rm H}_{n_i}] : [{\rm A}_i {\rm H}_{n_i - 1}^-] &= [{\rm H}^+] : K_{i1} \\ 82 | [{\rm A}_i {\rm H}_{n_i - 1}^-] : [{\rm A}_i {\rm H}_{n_i - 2}^{2-}] &= [{\rm H}^+] : K_{i2} \\ 83 | &\vdots \\ 84 | [{\rm A}_i {\rm H}^{(n_i - 1)-}] : [{\rm A}_i^{n_i-}] &= [{\rm H}^+] : K_{in_i} \\ 85 | \end{aligned} 86 | $$ 87 | 88 | のように濃度の比 $[{\rm A}_i {\rm H}_{n_i}] : [{\rm A}_i {\rm H}_{n_i - 1}^-]$,$[{\rm A}_i {\rm H}_{n_i - 1}^-] : [{\rm A}_i {\rm H}_{n_i - 2}^{2-}]$,……,$[{\rm A}_i {\rm H}^{(n_i - 1)-}] : [{\rm A}_i^{n_i-}]$ が分かるので,これらを合わせて比 $[{\rm A}_i {\rm H}_{n_i}] : [{\rm A}_i {\rm H}_{n_i - 1}^-] : [{\rm A}_i {\rm H}_{n_i - 2}^{2-}] : \cdots : [{\rm A}_i^{n_i-}]$ が求まります. 89 | 90 | 一方これらの濃度の合計 $[{\rm A}_i {\rm H}_{n_i}] + [{\rm A}_i {\rm H}_{n_i - 1}^-] + [{\rm A}_i {\rm H}_{n_i - 2}^{2-}] + \cdots + [{\rm A}_i^{n_i-}]$ は $c_i [\rm mol/L]$ であると分かっているので,比の情報と合わせることで濃度 $[{\rm A}_i {\rm H}_{n_i}]$,$[{\rm A}_i {\rm H}_{n_i - 1}^-]$,$[{\rm A}_i {\rm H}_{n_i - 2}^{2-}]$,……,$[{\rm A}_i^{n_i-}]$ が全て求まります. 91 | 92 | ここで, ${\rm A}_i {\rm H}_{n_i - 1}^-$ は $1$ 価, ${\rm A}_i {\rm H}_{n_i - 2}^{2-}$ は $2$ 価,……, ${\rm A}_i^{n_i-}$ は $n_i$ 価の陰イオンであることに着目すると,これらのもつ電荷の合計は 93 | 94 | $$ 95 | \sum_{j = 1}^{n_i} -j[{\rm A}_i {\rm H}_{n_i - j}^{j-}] = -1 [{\rm A}_i {\rm H}_{n_i - 1}^-] -2 [{\rm A}_i {\rm H}_{n_i - 2}^{2-}] - \cdots - n_i [{\rm A}_i^{n_i-}] 96 | $$ 97 | 98 | となります. $m$ 種類全ての酸についてこれを計算し,総和 99 | 100 | $$ 101 | S = \sum_{i = 1}^{m} \sum_{j = 1}^{n_i} -j[{\rm A}_i {\rm H}_{n_i - j}^{j-}] 102 | $$ 103 | 104 | を求めます. 105 | 106 | もし,最初に決め打ちした $\rm [H^+]$ の値が正しければ,水溶液は電気的に中性なので $[{\rm H}^+] + S = 0$ となるはずです. 107 | 108 | では,決め打ちした $\rm [H^+]$ の値が実際より大きかったらどうなるでしょうか?これは,いったん水溶液に強酸 $\rm AH$ を加えて,平衡に達したところで突然陰イオン $\rm A^-$ だけを取り除いた状況(実際にはあり得ないが)と同じなので, $[{\rm H}^+] + S > 0$ となります. 109 | 110 | 逆に $\rm [H^+]$ の値が実際より小さければ, $[{\rm H}^+] + S < 0$ となります. 111 | 112 | よって, $\rm [H^+]$ を決め打ちしたらそれが実際より大きいか小さいか分かるので,二分探索によって $[\rm H^+]$ を求めることができます. 113 | 114 | # おわりに 115 | この方法によって,様々な種類の酸が混ざった水溶液の $\rm pH$ を簡単に求めることができます.酸だけでなく塩基も含まれる場合や,塩を溶かす場合についても,同様に解くことができます. 116 | 117 | なんだか用途の限られた(?)記事になってしまいましたが,誰かの役に立てば幸いです. 118 | 119 | ~~誰かこれ読んでブラウザ上で簡単にシミュレーションができる Web サイト作って欲しい~~ 120 | なんと THF さんが作ってくださいました![ここ](https://www.e-na.space/%E6%BA%B6%E6%B6%B2%E3%82%B7%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF.html)で実行できます. -------------------------------------------------------------------------------- /articles/hiragana-substring.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Re: C言語でひらがなの部分文字列" 3 | emoji: "😃" 4 | type: "tech" # tech: 技術記事 / idea: アイデア 5 | topics: ["C"] 6 | published: true 7 | --- 8 | 9 | [この記事](https://zenn.dev/masakielastic/articles/fc63a2951b32c5)を見て,おお〜こりゃ便利だとなりました😂 10 | それで早速使ってみたら,変なことになってしまい……🥲 11 | 12 | ```cpp 13 | #include 14 | #include 15 | 16 | int main(void){ 17 | uint8_t *str = "ひらがな"; 18 | printf("%.*s\n", 9, str); 19 | } 20 | ``` 21 | 22 | 出力が「ひらか」になってしまったんですね😯 23 | 24 | > ひらがなの場合、1文字は3バイトなので、 25 | 26 | どうやら濁点が含まれるとそうとは限らないらしく,なるほどと🤔 27 | 28 | 同じ記事に書かれていた,`strndup` の方でも,うまく行かず……😭 29 | ```cpp 30 | #include 31 | #include 32 | #include 33 | 34 | int main(void){ 35 | uint8_t *src = "ひらがな"; 36 | uint8_t *dest = strndup(src, 9); 37 | printf("%s\n", dest); 38 | } 39 | ``` 40 | 41 | 調べていると,どうやらこういう場合は ICU (International Components for Unicode) というものを使うのが良いのだそうです✋😃 42 | 43 | コード例です👇 44 | ```cpp 45 | #include 46 | #include 47 | 48 | int main(void){ 49 | uint8_t *str = "ひらがな"; 50 | UErrorCode err = U_ZERO_ERROR; 51 | UText *text = utext_openUTF8(NULL, str, -1, &err); 52 | UBreakIterator *iter = ubrk_open(UBRK_CHARACTER, "ja_JP", NULL, 0, &err); 53 | ubrk_setUText(iter, text, &err); 54 | int32_t len = 0; 55 | for(int i = 0; i < 3; i++){ 56 | printf("%d文字目は%dバイト目から", i + 1, len + 1); 57 | len = ubrk_next(iter); 58 | printf("%dバイト目まで\n", len); 59 | } 60 | printf("%.*s\n", len, str); 61 | utext_close(text); 62 | ubrk_close(iter); 63 | } 64 | ``` 65 | 出力は以下のようになりました! 66 | ``` 67 | 1文字目は1バイト目から3バイト目まで 68 | 2文字目は4バイト目から6バイト目まで 69 | 3文字目は7バイト目から12バイト目まで 70 | ひらが 71 | ``` 72 | ちゃんと「ひらが」になっています😆 73 | 74 | 「が」は7バイト目から12バイト目までの6バイトだったんですね🤣 75 | -------------------------------------------------------------------------------- /articles/i3-window-manager.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "i3wm,config しないなんて勿体ない!" 3 | emoji: "🪟" 4 | type: "tech" 5 | topics: ["i3wm"] 6 | published: true 7 | --- 8 | 9 | # みなさん i3 window manager 使ってますか? 10 | 使ってますよね,**使い心地いい**ですもんね. 11 | 12 | [User's Guide](https://i3wm.org/docs/userguide.html) で説明されている**色々な機能**を使って config を書くと**本当に快適に**…… 13 | 14 | え? **config はデフォルトのまま**?? 15 | 16 | **勿体ない!!!** i3 は**好きに config を書き換えて自分の手に一番馴染むものを追求する**のが良いんですよ!!!(主張) 17 | 18 | # さぁ, i3 の真の魅力を引き出しましょう. 19 | 要約:この記事は [i3 User's Guide](https://i3wm.org/docs/userguide.html) の内容のうち,キーボードショートカット等に関する部分を日本語で説明したものです. 20 | 21 | i3 を設定するには, `/etc/i3/config` に用意されているデフォルト設定ファイルを `~/.i3/config` あるいは `~/.config/i3/config` にコピーし,編集します. 22 | 23 | i3 の設定ファイルの書き方はバージョンごとに違い,今の最新は v4 です.コメント( `#` で始まる行はコメントです)を用いて 24 | ``` 25 | # i3 config file (v4) 26 | ``` 27 | と書き, v4 であることを明記してください(でないと,古いバージョンで書かれているとみなされる可能性があります). 28 | 29 | 設定ファイル内では変数を使うことができます. `$` で始まるのが変数です. 30 | ``` 31 | set $hoge fuga 32 | ``` 33 | と書いておくと,ファイル内の `$hoge` が `fuga` で置き換わります. 34 | 35 | # キーバインド(ショートカットキーの設定) 36 | キーバインドは **`bindsym`** あるいは **`bindcode`** を使って設定します.前者はキーを keysym,後者は keycode で指定します.なので設定したいキーの keysym あるいは keycode を事前に知る必要があります. 37 | 38 | `xorg-docs` パッケージに入っている `X(7)` の `KEYBOARDS` セクションによれば, keycode は物理的な各キーの番号で,一方 keysym はそのキーが表す文字です. `$ xmodmap -pke` で keycode と keysym の対応一覧を見ることができます.たとえばエンターキーの keysym は `Return` です. 39 | 40 | 普通ショートカットキーは修飾キー(Ctrl や Alt)と一緒に使いますが,これはたとえば前に `Mod1+` を付けて `Mod1+Return` とすることで Alt+エンターになります.前に付けられる修飾キーは `Control` `Shift` `Mod1` `Mod2` `Mod3` `Mod4` `Mod5` です(`Mod1` が Alt, `Mod4` が Super). 41 | 42 | ``` 43 | bindsym Mod1+Return exec i3-sensible-terminal 44 | ``` 45 | これは Alt+エンターでターミナルが起動するように設定しています.たとえばエンターの keycode が 36 なら 46 | ``` 47 | bindcode Mod1+36 exec i3-sensible-terminal 48 | ``` 49 | としても同じです. 50 | 51 | keysym / keycode の前に `--release` を付けると,キーを押したタイミングではなく,離したタイミングでコマンドが実行されます. 52 | ``` 53 | bindsym --release Mod1+Return exec i3-sensible-terminal 54 | ``` 55 | 56 | # マウスバインド 57 | マウスバインドは,ウィンドウのどこかをクリックしたときに実行するショートカットです.キーバインドと同じ **`bindsym`** を使います. 58 | 59 | 自分の環境では,左クリックが `button1`,中央(ホイール)クリックが `button2`,右クリックが `button3`,ホイールのスクロールが `button4` / `button5` なんですがこれって共通なのかな?一応 keycode / keysym と一緒に `xev` (キーボード/ポインタイベントの情報をリアルタイムで逐一出力してくれるツール)で調べられます. 60 | 61 | デフォルトでは,タイトルバーがクリックされたときに反応します. 62 | ``` 63 | bindsym button3 floating toggle 64 | ``` 65 | こうすると,タイトルバーで右クリックしたときに,ウィンドウがタイル状態と自由に動かせるフロート状態の間で切り替わります.マウスバインドと同様に修飾キーを付けることもできます. 66 | 67 | ``` 68 | bindsym Mod1+button3 floating toggle 69 | ``` 70 | 71 | `--release` を付けると離したときに反応します. 72 | ``` 73 | bindsym --release button2 kill 74 | ``` 75 | タイトルバーでホイールボタンを離したときに,画面が閉じます. 76 | 77 | タイトルバーだけでなくウィンドウ全体が反応するようにしたければ, `--whole-window` を指定します. 78 | ``` 79 | bindsym --whole-window Mod1+button2 kill 80 | ``` 81 | ウィンドウ内のどこかで `Mod1+button2` を押すことで画面が閉じます.ただしこのときウィンドウのボーダー(境界線)だけは含まれないので,それも含めるには `--border` を追加します.また,逆にタイトルバーを除外するには `--exclude-titlebar` を指定します. 82 | 83 | # モード 84 | **`mode`** を使うとモードを定義することができます.モードとは次のようなものです. 85 | 86 | - キーバインド/マウスバインドを用いてモード間を移動することができる. 87 | - 各キーバインド/マウスバインドは,特定のモードでのみ有効になる 88 | 89 | `/etc/i3/config` を見ると, resize というモードが定義されています. 90 | 91 | ```:/etc/i3/config 92 | mode "resize" { 93 | (略) 94 | bindsym Left resize shrink width 10 px or 10 ppt 95 | bindsym Down resize grow height 10 px or 10 ppt 96 | bindsym Up resize shrink height 10 px or 10 ppt 97 | bindsym Right resize grow width 10 px or 10 ppt 98 | (略) 99 | bindsym Return mode "default" 100 | bindsym Escape mode "default" 101 | bindsym Mod1+r mode "default" 102 | } 103 | 104 | bindsym Mod1+r mode "resize" 105 | ``` 106 | 107 | `bindsym Mod1+r mode "resize"` により, `Mod1+r` を押して resize モードに入ることができます. resize モードでは `mode "resize"` のブロック内に書かれているキーバインド/マウスバインドのみが有効になり,上下左右キーを用いてウィンドウの大きさを変えることができます.`bindsym Return mode "default"` により, `Return` で resize モードを抜けることができます. default モードは初めから用意されているモードで,モードを指定せずに書いたキーバインド/マウスバインドは全て default モードに属しています. 108 | 109 | あるモードに入ると,そのモード内で定義されたキーバインド/マウスバインド以外全部無効になってしまうため注意が必要です.特に, default モードに戻る手段を書き忘れると大変なことになります. `Return` でも `Escape` でも `Mod1+r` (モードに入るときのキーバインドと同じにしておく)でもいいので必ず書きましょう. 110 | 111 | さて,モードを使うと使えるキーバインドを一気に増やすことができます!キーバインド同士の被りを避けるために `Ctrl` `Alt` `Shift` 等の組み合わせに苦心しなくても,たとえば 112 | ``` 113 | bindsym Mod4+l mode "layout" 114 | mode "layout" { 115 | bindsym Return mode "default" 116 | bindsym h layout splith; mode "default" 117 | bindsym v layout splitv; mode "default" 118 | bindsym s layout stacking; mode "default" 119 | bindsym t layout tabbed; mode "default" 120 | bindsym f floating toggle; mode "default" 121 | } 122 | ``` 123 | のように書いておくだけで,ウィンドウを横に並べる `Mod4+l` `h`,縦に並べる `Mod4+l` `v`,スタック状に重ねる `Mod4+l` `s`,タブ状に重ねる `Mod4+l` `t`,タイル/フロート間で切り替える `Mod4+l` `f` と一度に 5 つのコマンドが定義できるわけです(※セミコロン `;` で区切るのはシェルと同じで,複数のコマンドを順に実行できます).押すキーの個数は増えますが,単純計算で使えるキーの組み合わせが 26 倍に増えるため被りなんて気にしないでよくなります! 124 | 125 | しかしこうやってキーバインドを増やしすぎると,自分でどう設定したか分からなくなってしまいますね.そういうときは,モードに入ったときモードの名前がステータスバーに表示されることを利用して,名前を `"layout"` から `"layout [h: splith] [v: splitv] [s: stacking] [t: tabbed] [f: floating toggle]"` のようなものに変えておくと便利です.長いので変数でおいておくといいでしょう. 126 | 127 | ``` 128 | set $layout "layout [h: splith] [v: splitv] [s: stacking] [t: tabbed] [f: floating toggle]" 129 | bindsym Mod4+l mode $layout 130 | mode $layout { 131 | bindsym Return mode "default" 132 | bindsym h layout splith; mode "default" 133 | bindsym v layout splitv; mode "default" 134 | bindsym s layout stacking; mode "default" 135 | bindsym t layout tabbed; mode "default" 136 | bindsym f floating toggle; mode "default" 137 | } 138 | ``` 139 | 140 | # コマンド 141 | `bindsym` / `bindcode` でキーやボタンに対応させることのできるコマンドとして,ここまで `exec` や `kill` や `floating toggle` などが登場しましたが,他にも多くのコマンドが存在します. 142 | 143 | まずいくつか用語を説明します.複数のウィンドウはある**コンテナ**に属することでタイル状に並びます.コンテナは**水平**か**垂直**いずれかの状態をもち,水平なコンテナに属するウィンドウは横に,垂直なコンテナに属するウィンドウは縦に並びます.コンテナがさらに他のコンテナに属していれば,画面が左右に分かれ,うち右半分がさらに上下に分かれるということもできます(水平なコンテナの中に垂直なコンテナがある).ウィンドウ自体もコンテナです.コンテナ A がコンテナ B を含むとき, A を B の**親**, B を A の**子**といいます.常にいずれか 1 つのコンテナが**フォーカス**されており, `kill` や `resize` などのコマンドはフォーカスされたコンテナに作用します. 144 | 145 | 以下, `$mod` は `Mod1` や `Mod4` などの修飾キーを指す変数とします. 146 | ## `exec ` 147 | `exec` はコマンドをシェルに渡して実行します. 148 | ``` 149 | bindsym $mod+g exec gimp 150 | ``` 151 | `Mod1+g` を押すと,シェル上で `$ gimp` を実行したのと同じことになります. 152 | 153 | ## `split vertical` `split horizontal` `split toggle` 154 | `split` は,フォーカスされたコンテナ A を, A を子としてもつコンテナ X に変えます.その後 X に属する新しいウィンドウ B を作れば, A と B は縦か横に並ぶことになります.コンテナの状態は, `split vertical` であれば垂直, `split horizontal` であれば水平になります. `split toggle` は垂直/水平を入れ替えます. 155 | ``` 156 | bindsym $mod+v split vertical 157 | bindsym $mod+h split horizontal 158 | bindsym $mod+t split toggle 159 | ``` 160 | ## `layout splitv` `layout splith` `layout stacking` `layout tabbed` 161 | `layout` は,フォーカスされたコンテナ(またはフォーカスされたウィンドウが属するコンテナ)のレイアウトを変更します.`splitv` は垂直で,ウィンドウが縦に並びます.`splith` は水平で,ウィンドウが横に並びます.`stacking` は垂直ですがウィンドウ本体は重なり,タイトルバーだけが縦に並びます. `tabbed` も水平ですがウィンドウ本体は重なり,タイトルバーだけが横(タブ状)に並びます. 162 | ``` 163 | bindsym $mod+s layout stacking 164 | bindsym $mod+w layout tabbed 165 | ``` 166 | 167 | `layout toggle` でこれらのレイアウトを順に切り替えることができます. 168 | ``` 169 | bindsym $mod+x layout toggle stacking tabbed splith 170 | ``` 171 | これで `$mod+x` を押すたびに `stacking`→`tabbed`→`splith`→`stacking`→……の順に切り替わります. `toggle all` なら `stacking`→`tabbed`→`splith`→`splitv`→`stacking`, `toggle` だけなら `stacking`→`tabbed`→`split`(`splith`または`splitv`のうち最後に使われていた方)になります. 172 | ## `fullscreen enable` `fullscreen disable` `fullscreen toggle` 173 | `fullscreen enable` でフルスクリーンになり, `fullscreen disable` で解除されます. `fullscreen toggle` は 2 つを切り替えます. 174 | ## `floating enable` `floating disable` `floating toggle` 175 | `floating enable` でフロート状になり, `floating disable` でタイル状になります. `floating toggle` は 2 つを切り替えます. 176 | ## `focus (対象)` 177 | フォーカスの対象を変えるには `focus` を使います. 178 | 179 | `focus left` で左,`focus right` で右,`focus up` で上,`focus down` で下,`focus parent` で親,`focus child` で子のコンテナをフォーカスします.`focus next` は水平なら右,垂直なら下になり, `focus prev` は水平なら左,垂直なら上になります. 180 | 181 | `focus floating` は最後にフォーカスしていたフロート状のコンテナ,`focus tiling` は最後にフォーカスしていたタイル状のコンテナをフォーカスし,`mode_toggle` はこれらの間で切り替わります. 182 | ``` 183 | bindsym $mod+j focus left 184 | bindsym $mod+k focus down 185 | bindsym $mod+l focus up 186 | bindsym $mod+semicolon focus right 187 | bindsym $mod+u focus parent 188 | bindsym $mod+g focus mode_toggle 189 | ``` 190 | また,ウィンドウのクラス(`xprop` コマンドの `WM_CLASS`)などを用いて特定のウィンドウを指定してフォーカスすることもできます. 191 | ``` 192 | bindsym $mod+F1 [class="Firefox"] focus 193 | ``` 194 | これはブラウザ firefox のウィンドウをフォーカスします. 195 | ## `move (方向)` `move position (位置)` 196 | 画面上でウィンドウを移動させるには `move` を使います. 197 | 198 | `move left` で左,`move right` で右,`move down` で下,`move up` で上に移動します.デフォルトの移動量は 10 px ですが, `move left 20 px` のようにして指定もできます. 199 | 200 | `move position 200 px 100 px` で座標 (200 px, 100 px) の点に移動します. `move position center` で画面の中心, `move position mouse` でマウスのある位置に移動します. 201 | 202 | ``` 203 | bindsym $mod+j move left 204 | bindsym $mod+k move down 205 | bindsym $mod+l move up 206 | bindsym $mod+semicolon move right 207 | ``` 208 | ## `workspace ` `workspace prev` `workspace next` 209 | ワークスペースは, Mac の操作スペースとか Windows の仮想デスクトップと同じようなものです. 210 | 211 | `workspace 1` はワークスペース 1 を開きます.`workspace browser` や `workspace slack` など好きな名前が使えます. `workspace next` `workspace prev` を使ってワークスペース間を順次切り替えることができます.直前に開いていたワークスペースに戻るには `workspace back_and_forth` とします. 212 | 213 | ## `move container to workspace ` 214 | コンテナを特定のワークスペースに移動します.これも名前の代わりに `prev` `next` が使えます. 215 | ## `rename workspace to ` 216 | ワークスペースの名前を変更します.古い名前を省略すると,今いるワークスペースの名前が変更されます. 217 | ## `resize grow (方向)` `resize shrink (方向)` `resize set (幅) (高さ)` 218 | `resize` は,フォーカスされたウィンドウの大きさを変更します. 219 | 220 | `resize grow up` は上に,`resize grow down` は下に,`resize grow left` は左に,`resize grow right` は右に大きくなります.`shrink` は逆に小さくなります.デフォルトの変化量は 10 px ですが, `resize grow up 20 px` のようにして指定もできます. 221 | 222 | `resize set 640 px 480 px` で幅 640 px 高さ 480 px になります. 223 | ## `border normal` `border pixel` `border none` 224 | `border` は,フォーカスされたウィンドウの境界線のスタイルを変更します. 225 | 226 | `border normal` は普通の境界線で,タイトルバーも表示されます.`border pixel` だと,ウィンドウを囲む枠はありますがタイトルバーはありません.`border none` は境界線なしです.`border pixel 1` のようにして枠線の太さを指定できます.`border normal 0` ならタイトルバーは表示されますが枠はありません. 227 | ``` 228 | bindsym $mod+t border normal 0 229 | bindsym $mod+y border pixel 3 230 | ``` 231 | ## `reload` `restart` `exit` 232 | `reload` は config を再読込みします.`restart` は i3 を再起動し,`exit` は i3 を終了します. 233 | ``` 234 | bindsym $mod+Shift+r restart 235 | bindsym $mod+Shift+w reload 236 | bindsym $mod+Shift+e exit 237 | ``` 238 | ## `mark ` 239 | `mark` を使うと,フォーカスされたコンテナにマークを付け, `focus` コマンド等で利用することができます. 240 | 241 | `mark X` でコンテナに `X` というマークが付きます.その後, `focus` コマンドで `[con_mark="X"] focus` とするといつでもそのコンテナに戻ってくることができます. 242 | ``` 243 | # 今のコンテナに mark-A というマークを付ける 244 | bindsym Mod4+a mark mark-A 245 | # mark-A というマークが付いたコンテナをフォーカスする 246 | bindsym Mod4+Shift+a [con_mark="mark-A"] focus 247 | ``` 248 | 249 | # i3 は多機能! 250 | i3 にはこんなに**多くの機能**があるんだから使わなきゃ勿体ない!今すぐ**あなただけの config** を書いて**幸せ**になりましょう!!! 251 | 252 | では!!!!! -------------------------------------------------------------------------------- /articles/latex-github-actions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "GitHub ActionsでLaTeX自動コンパイル" 3 | emoji: "📝" 4 | type: "tech" # tech: 技術記事 / idea: アイデア 5 | topics: ["github", "latex"] 6 | published: true 7 | --- 8 | 9 | # 目標 10 | 11 | GitHub に LaTeX ソースファイルを push すると自動で PDF へとコンパイルされる環境を作ります. 12 | 13 | # 準備 14 | Git リポジトリを用意します.編集作業を行うブランチ名は,main とします. 15 | ```sh 16 | $ git init -b main 17 | $ git remote add origin (URL) 18 | ``` 19 | 20 | # main ブランチの編集 21 | とりあえず今回は以下の `sample.tex` を作っておきます. 22 | ```tex:sample.tex 23 | \documentclass{article} 24 | \begin{document} 25 | Hello, world! 26 | \end{document} 27 | ``` 28 | `$ latexmk -pdf` でコンパイルできることを確認します. 29 | 30 | LaTeX はコンパイル毎にたくさんのファイルを生成しますが,多分全部ソースファイルの名前にならって `sample.*` になると思います.よって `sample.tex` だけを管理するために以下のようにします. 31 | ```:.gitignore 32 | sample.* 33 | !sample.tex 34 | ``` 35 | 36 | ここまでの変更を GitHub 上に push します. 37 | ```sh 38 | $ git add . 39 | $ git commit -m "Initial commit" 40 | $ git push -u origin main 41 | ``` 42 | 43 | # pdf ブランチの作成 44 | 出来上がった PDF ファイルの push 先となる pdf ブランチを作ります. 45 | ```sh 46 | $ git checkout -b pdf 47 | ``` 48 | 49 | こちらは `sample.*` を ignore したくないので,`.gitignore` を書き換えます.ただし,LaTeX が自動的に生成する `sample.aux` は,前のものが残っているとコンパイルが失敗することがあるので,これだけ ignore します. 50 | ```:.gitignore 51 | sample.aux 52 | ``` 53 | この状態で GitHub 上に push します. 54 | ```sh 55 | $ git add .gitignore 56 | $ git commit -m "Created branch pdf" 57 | $ git push origin pdf 58 | ``` 59 | 60 | # GitHub Actions の利用 61 | main ブランチに戻り,`.github/workflows`下の yml ファイルにワークフローを記述します. 62 | ```sh 63 | $ git checkout main 64 | $ mkdir -p .github/workflows 65 | ``` 66 | ```yml:.github/workflows/latex.yml 67 | name: LaTeX compilation 68 | on: 69 | push: 70 | branches: 71 | - main 72 | jobs: 73 | build: 74 | runs-on: ubuntu-latest 75 | steps: [ ここに記述 ] 76 | ``` 77 | 78 | Action 内で Git リポジトリの中身に対する操作を行うときは,actions/checkout を利用します. 79 | ```yml 80 | - name: Set up Git repository 81 | uses: actions/checkout@v3 82 | with: 83 | fetch-depth: 0 84 | ``` 85 | `fetch-depth: 0` は,main ブランチだけでなく pdf ブランチも fetch するよう指定しています. 86 | 87 | 次に,Git 環境を整え,main ブランチを pdf ブランチに merge します. 88 | ```yml 89 | - name: Merge main branch 90 | run: | 91 | git config user.name fiveseven-lambda 92 | git config user.email fiveseven.lambda@gmail.com 93 | git checkout pdf 94 | git merge main 95 | ``` 96 | 97 | そして TeX ファイルをコンパイルします.[GitHub Marketplace](https://github.com/marketplace)で検索すると,xu-cheng/latex-action というものが出てくるので,これを使います. 98 | ```yml 99 | - name: Compile LaTeX document 100 | uses: xu-cheng/latex-action@v2 101 | with: 102 | root_file: 103 | sample.tex 104 | ``` 105 | デフォルトで,`latexmk` の `-pdf` オプションで PDF へコンパイルされます. 106 | 107 | 最後に,できあがった PDF ファイルを pdf ブランチに push します. 108 | ```yml 109 | - name: Push PDF file 110 | run: | 111 | git add . 112 | git commit -m "LaTeX compilation" 113 | git push origin pdf 114 | ``` 115 | 116 | 全体では以下のようになります. 117 | ```yml:.github/workflows/latex.yml 118 | name: LaTeX compilation 119 | on: 120 | push: 121 | branches: 122 | - main 123 | jobs: 124 | build: 125 | runs-on: ubuntu-latest 126 | steps: 127 | - name: Set up Git repository 128 | uses: actions/checkout@v3 129 | with: 130 | fetch-depth: 0 131 | - name: Merge main branch 132 | run: | 133 | git config user.name fiveseven-lambda 134 | git config user.email fiveseven.lambda@gmail.com 135 | git checkout pdf 136 | git merge main 137 | - name: Compile LaTeX document 138 | uses: xu-cheng/latex-action@v2 139 | with: 140 | root_file: 141 | sample.tex 142 | - name: Push PDF file 143 | run: | 144 | git add . 145 | git commit -m "LaTeX compilation" 146 | git push origin pdf 147 | ``` 148 | 149 | これを push すれば終わりです. 150 | ```sh 151 | $ git add . 152 | $ git commit -m "Added workflow for LaTeX compilation" 153 | $ git push 154 | ``` 155 | 156 | これ以降は,`sample.tex` を編集して main に commit,push するだけで OK です! -------------------------------------------------------------------------------- /articles/line-break-in-string-literal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "文字列リテラル中で改行できる言語,できない言語" 3 | emoji: "💬" 4 | type: "tech" # tech: 技術記事 / idea: アイデア 5 | topics: [プログラミング言語] 6 | published: true 7 | --- 8 | 9 | 表にしました. 10 | 11 | | 言語 | 文字列リテラル中での改行 | 12 | | -- | -- | 13 | | Perl | 可 | 14 | | PHP | 可 | 15 | | Ruby | 可 | 16 | | D | 可 | 17 | | Rust | 可 | 18 | | Julia | 可 | 19 | | Bash, Zsh 等 | 可 | 20 | | Java | `"""` 〜 `"""` で可 | 21 | | Python | `"""` 〜 `"""` 等で可 | 22 | | Elm | `"""` 〜 `"""` で可 | 23 | | C | 不可 | 24 | | C++ | 不可(生文字列リテラルで可) | 25 | | C# | 不可(生文字列リテラル等で可) | 26 | | Go | 不可(生文字列リテラルで可) | 27 | | Lua | 不可(長括弧で可) | 28 | | JavaScript | 不可(テンプレートリテラルで可) | 29 | 30 | 他に,改行周りで面白い仕様の言語があったら教えてほしいです. 31 | # 補足 32 | ## Java 33 | Java15 以降では,`"` 〜 `"` の代わりに `"""` 〜 `"""` を使うと改行を含められます. 34 | 35 | ## Python 36 | Python では,`'` 〜 `'` や `"` 〜 `"` の代わりに `'''` 〜 `'''` や `"""` 〜 `"""` を使うと改行を含められます. 37 | 38 | ちなみに,前に `r` か `R` を付けると生文字列リテラル,`f` か `F` を付けるとフォーマット済み文字列リテラルです. 39 | ## Elm 40 | フォロワーに教えてもらいました. 41 | ## C++ 42 | C++11 から raw string literal `R"` 〜 `"` が導入され,`(` 〜 `)` の中でエスケープが無効になります.このとき,改行も含められるようになります. 43 | ## C# 44 | C# の `@` は「逐語的に解釈しろ」という意味で,文字列リテラル `"` 〜 `"` の前に付けて `@"` 〜 `"` とするとエスケープが無効になります.このとき,改行も含められるようになります. 45 | 46 | また,C# 11 で導入された生文字列リテラル `"""` 〜 `"""` を使うと,エスケープが無効になることと改行が含められること以外に,インデントまでよしなにされます.これは嬉しい. 47 | ## Go 48 | Go の生文字列リテラルはバッククォート \` 〜 \` で,エスケープが無効になるとともに改行が含められます. 49 | ## Lua 50 | これもフォロワーに教えてもらいました.Lua の生文字列リテラル相当(エスケープ無効 & 改行可)は `[[` 〜 `]]` だそうです. 51 | ## JavaScript 52 | JavaScript はバッククォート \` 〜 \` でくくってテンプレートリテラルを書けます.これは,`${` 〜 `}` 中に式を埋め込めるというものです(Python のフォーマット済み文字列に相当).このとき,改行も含められるようになります. -------------------------------------------------------------------------------- /articles/niu-graph-rust.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rust で抽象化したグラフを扱う" 3 | emoji: "🪩" 4 | type: "tech" # tech: 技術記事 / idea: アイデア 5 | topics: ["rust"] 6 | published: false 7 | --- 8 | 9 | https://zenn.dev/toga/articles/rust-graph-trait -------------------------------------------------------------------------------- /articles/parse-infix-notation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "軽実装!左結合中置演算子の優先順位付パース" 3 | emoji: "➗" 4 | type: "tech" 5 | topics: ["構文解析", "cpp"] 6 | published: true 7 | --- 8 | 9 | 中置演算子の構文解析が面倒みたいに言っている人を見かけたので,そんなことないよ〜という記事です.使う言語は C++17 です. 10 | # パーサの仕様 11 | 字句解析やカッコの解析等の要素を全て省いて,左結合の中置演算子の構文解析にのみ着目するために,今回作るパーサは非常に単純化された以下のような式を読みます. 12 | 13 | - 変数は英大文字 `A`-`Z` あるいは英小文字 `a`-`z` の 1 文字のみ. 14 | - 2 項演算子は `*` `/` `+` `-` `<` `>` `&` `|` の 8 種類. 15 | - 優先順位は高い方から `*` `/` → `+` `-` → `<` `>` → `&` → `|` の順. 16 | - 全て左結合. 17 | - 余計な空白を含まない. 18 | 19 | BNF で書くと次のようになります. 20 | ``` 21 | ::= [A-Za-z] 22 | ::= | '*' | '/' 23 | ::= | '+' | '-' 24 | ::= | '<' | '>' 25 | ::= | '&' 26 | ::= | '|' 27 | ``` 28 | # 構文木 29 | パーサは文字列を構文木に変換します.今回作る構文木は,全ての頂点が「子をもたない」「ちょうど 2 つの子をもつ」のどちらかであるような木構造であり,以下のように基底クラス `Expr` と 2 つの派生クラス `Id`,`Binary` として定義できます. 30 | ```cpp 31 | #include 32 | #include 33 | 34 | // 基底クラス 35 | class Expr { 36 | public: 37 | virtual ~Expr() = default; 38 | }; 39 | 40 | // 1 文字の変数/子をもたない頂点 41 | class Id : public Expr { 42 | char name; // 変数名 43 | public: 44 | Id(char name): name(name) {} 45 | }; 46 | 47 | // 2 項演算子 48 | enum class Op { 49 | Mul, // '*' 50 | Div, // '/' 51 | Add, // '+' 52 | Sub, // '-' 53 | Less, // '<' 54 | Greater, // '>' 55 | And, // '&' 56 | Or, // '|' 57 | }; 58 | 59 | // 2 項演算/子を 2 つもつ頂点 60 | class Binary : public Expr { 61 | std::unique_ptr left, right; 62 | Op op; 63 | public: 64 | Binary(std::unique_ptr left, std::unique_ptr right, Op op): 65 | left(std::move(left)), 66 | right(std::move(right)), 67 | op(op) {} 68 | }; 69 | ``` 70 | # 確認用の出力 71 | ちゃんとパースされたか確認するときのために出力用のメンバ関数 `print(int indent)` も用意しておきます.引数 `indent` は見やすくするためのインデントです. 72 | ```cpp 73 | class Expr { 74 | /* 略 */ 75 | public: 76 | virtual void print(int = 0) = 0; // これを追加 77 | }; 78 | 79 | class Id : public Expr { 80 | /* 略 */ 81 | public: 82 | void print(int) override; // これを追加 83 | }; 84 | 85 | class Binary : public Expr { 86 | /* 略 */ 87 | public: 88 | void print(int) override; // これを追加 89 | }; 90 | 91 | constexpr char TAB[] = " "; 92 | 93 | void Id::print(int indent) { 94 | for(int i = 0; i < indent; ++i) std::cout << TAB; 95 | std::cout << name << std::endl; 96 | } 97 | 98 | void Binary::print(int indent) { 99 | for(int i = 0; i < indent; ++i) std::cout << TAB; 100 | std::string name; 101 | switch(op){ 102 | case Op::Mul: name = "mul"; break; 103 | case Op::Div: name = "div"; break; 104 | case Op::Add: name = "add"; break; 105 | case Op::Sub: name = "sub"; break; 106 | case Op::Less: name = "less"; break; 107 | case Op::Greater: name = "greater"; break; 108 | case Op::And: name = "and"; break; 109 | case Op::Or: name = "or"; 110 | } 111 | std::cout << name << std::endl; 112 | left->print(indent + 1); 113 | right->print(indent + 1); 114 | } 115 | ``` 116 | これで,たとえば `a+b-c*d` のパースが正しくできていれば 117 | ``` 118 | sub 119 | add 120 | a 121 | b 122 | mul 123 | c 124 | d 125 | ``` 126 | と出力されるようになります. 127 | # `*` `/` のみの場合 128 | まず,含まれる 2 項演算子が `*` と `/` の 2 種類のみの場合について考えます.BNF で言うと `` のみをパースすることになります.`*` と `/` の優先順位は同じなので,たとえば `a*b/c*d` であれば 129 | ``` 130 | mul 131 | div 132 | mul 133 | a 134 | b 135 | c 136 | d 137 | ``` 138 | となります.これを `parse_term` 関数として実装します.簡単のため,文字列を引数として受け取るような形ではなく,関数内で直接 `std::cin` から 1 文字ずつ読む形にします. 139 | ```cpp 140 | std::unique_ptr parse_term(); 141 | ``` 142 | 143 | 優先順位を気にしなくてよいため,まず返り値をもつ `ret` という変数を用意しておいて,次に式を前から順に読みながら `ret` を順次書き換えてゆくだけでできます. 144 | ```cpp 145 | std::unique_ptr parse_term() { 146 | // 式の先頭は単一の変数 147 | std::unique_ptr ret = std::make_unique(std::cin.get()); 148 | for(;;){ 149 | Op op; 150 | switch(std::cin.peek()){ 151 | case '*': op = Op::Mul; break; 152 | case '/': op = Op::Div; break; 153 | default: return ret; 154 | } 155 | std::cin.get(); // '*' か '/' を読む 156 | ret = std::make_unique( 157 | // 今までの ret は左の被演算子になる 158 | std::move(ret), 159 | // 右の被演算子は常に単一の変数 160 | std::make_unique(std::cin.get()), 161 | op 162 | ); 163 | } 164 | } 165 | ``` 166 | # 優先順位のある場合 167 | 次に,8 種類すべての 2 項演算子を入れてパースします.すると今度は,`` のパースの中で `` のパースを使い,`` のパースの中で `` のパースを使い,……というように,``→``→``→``→`` の順でパース関数が別のパース関数を呼び出すことになります.この 5 つを全て別の関数として書くと大変なので,1 つの再帰関数として実装します. 168 | 169 | といっても基本的には変わらなくて, 170 | - 5 段階のうち何段階目のパースをしているか表す引数 `precedence` がくわわる 171 | - `switch` 文の中身が「今の段階で解析の対象となる 2 項演算子」の判別に変わる. 172 | - `parse_term` 関数において `std::make_unique(std::cin.get())` だった部分が再帰呼出しになる. 173 | 174 | 結局全体では次のようになります. 175 | ```cpp 176 | #include 177 | 178 | std::unique_ptr parse(int precedence = 0) { 179 | if(precedence == 5){ 180 | // 再帰のベースケース:単一の変数 181 | return std::make_unique(std::cin.get()); 182 | } 183 | std::unique_ptr ret = parse(precedence + 1); // 再帰呼出し 184 | for(;;){ 185 | std::optional op; 186 | switch(std::cin.peek()){ 187 | case '*': 188 | if(precedence == 4) op = Op::Mul; 189 | break; 190 | case '/': 191 | if(precedence == 4) op = Op::Div; 192 | break; 193 | case '+': 194 | if(precedence == 3) op = Op::Add; 195 | break; 196 | case '-': 197 | if(precedence == 3) op = Op::Sub; 198 | break; 199 | case '<': 200 | if(precedence == 2) op = Op::Less; 201 | break; 202 | case '>': 203 | if(precedence == 2) op = Op::Greater; 204 | break; 205 | case '&': 206 | if(precedence == 1) op = Op::And; 207 | break; 208 | case '|': 209 | if(precedence == 0) op = Op::Or; 210 | break; 211 | } 212 | if(!op){ 213 | // 今の段階で解析の対象となる演算子ではない 214 | return ret; 215 | } 216 | std::cin.get(); 217 | ret = std::make_unique( 218 | std::move(ret), 219 | parse(precedence + 1), // 再帰呼出し 220 | op.value() 221 | ); 222 | } 223 | } 224 | ``` 225 | # コード全体 226 | 今回のパーサの全体像です. 227 | ```cpp 228 | #include 229 | #include 230 | #include 231 | 232 | class Expr { 233 | public: 234 | virtual ~Expr() = default; 235 | virtual void print(int = 0) = 0; 236 | }; 237 | 238 | class Id : public Expr { 239 | char name; 240 | public: 241 | Id(char name): name(name) {} 242 | void print(int) override; 243 | }; 244 | 245 | enum class Op { 246 | Mul, Div, 247 | Add, Sub, 248 | Less, Greater, 249 | And, 250 | Or, 251 | }; 252 | 253 | class Binary : public Expr { 254 | std::unique_ptr left, right; 255 | Op op; 256 | public: 257 | Binary(std::unique_ptr left, std::unique_ptr right, Op op): 258 | left(std::move(left)), 259 | right(std::move(right)), 260 | op(op) {} 261 | void print(int) override; 262 | }; 263 | 264 | std::unique_ptr parse(int precedence = 0) { 265 | if(precedence == 5) return std::make_unique(std::cin.get()); 266 | std::unique_ptr ret = parse(precedence + 1); 267 | for(;;){ 268 | std::optional op; 269 | switch(std::cin.peek()){ 270 | case '*': 271 | if(precedence == 4) op = Op::Mul; 272 | break; 273 | case '/': 274 | if(precedence == 4) op = Op::Div; 275 | break; 276 | case '+': 277 | if(precedence == 3) op = Op::Add; 278 | break; 279 | case '-': 280 | if(precedence == 3) op = Op::Sub; 281 | break; 282 | case '<': 283 | if(precedence == 2) op = Op::Less; 284 | break; 285 | case '>': 286 | if(precedence == 2) op = Op::Greater; 287 | break; 288 | case '&': 289 | if(precedence == 1) op = Op::And; 290 | break; 291 | case '|': 292 | if(precedence == 0) op = Op::Or; 293 | break; 294 | } 295 | if(!op) return ret; 296 | std::cin.get(); 297 | ret = std::make_unique( 298 | std::move(ret), 299 | parse(precedence + 1), 300 | op.value() 301 | ); 302 | } 303 | } 304 | 305 | int main(){ 306 | parse()->print(); 307 | } 308 | 309 | constexpr char TAB[] = " "; 310 | 311 | void Id::print(int indent) { 312 | for(int i = 0; i < indent; ++i) std::cout << TAB; 313 | std::cout << name << std::endl; 314 | } 315 | 316 | void Binary::print(int indent) { 317 | for(int i = 0; i < indent; ++i) std::cout << TAB; 318 | std::string name; 319 | switch(op){ 320 | case Op::Mul: name = "mul"; break; 321 | case Op::Div: name = "div"; break; 322 | case Op::Add: name = "add"; break; 323 | case Op::Sub: name = "sub"; break; 324 | case Op::Less: name = "less"; break; 325 | case Op::Greater: name = "greater"; break; 326 | case Op::And: name = "and"; break; 327 | case Op::Or: name = "or"; 328 | } 329 | std::cout << name << std::endl; 330 | left->print(indent + 1); 331 | right->print(indent + 1); 332 | } 333 | ``` 334 | ね,決して実装は重くないでしょう? -------------------------------------------------------------------------------- /articles/rust-for-beginner.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "そもそもプログラミング経験自体無い人がRustを学ぶとき,どんな順序が良いのか" 3 | emoji: "🦀" 4 | type: "idea" # tech: 技術記事 / idea: アイデア 5 | topics: ["Rust"] 6 | published: true 7 | --- 8 | 9 | 遠きに行くには必ず邇きよりす.高きに登るには必ず卑きよりす.何事にも順序というものがあります.Rust の学習もそうです. 10 | 11 | そこで,前提知識がほぼ無い状態から Rust を学ぶときに,どんな順序が良いのか,考えてみました. 12 | 13 | 1. コンパイル時と実行時の区別. 14 | Rust を学ぶとき,**何がコンパイル時に起こって何が実行時に起こるか**分からないと困ります.特に,型検査と借用検査がコンパイル時に行われることは,それらの基本的な規則を知る際に大切です.そこで,最初に Hello world を書く時点で,コンパイル→実行という流れを押さえておくべきでしょう. 15 | 1. コンパイルエラーの読み方. 16 | **まずコンパイルエラーを読む**という基本的な姿勢を身に付けるのは大切です. 17 | 1. 公式ドキュメントの場所. 18 | **まず公式ドキュメントを読む**という基本的な姿勢を身に付けるのも大切です. 19 | 1. Hello world 周辺の基本文法. 20 | 例えば以下の項目です. 21 | - `fn main() { }` が要ること. 22 | - `println!` マクロ. 23 | - 文字列リテラル. 24 | - 整数と小数の,算術演算 (`+` `-` `*` `/` `%`) と出力. 25 | - コメント. 26 | 1. 変数と型. 27 | **`mut` 変数はまだ難しいので,今の段階では immutable な変数のみ扱いましょう.**`for` 式が出てくるまで,`mut` 変数の存在は知らなくて構いません. 28 | 29 | ここは,コンパイル時と実行時の区別が重要となる最初の場面です.例えば,全ての型推論がコンパイル時にできなければいけないことを理解しましょう. 30 | 31 | 後で参照や所有権の話が出てくると,メモリに関する最低限のイメージが要求されるので,その基本をここで押さえると良いでしょう.ただし,スタックは再帰関数が登場してからで良いでしょう. 32 | 1. `if` 式. 33 | 自分は競技プログラミング向けの記事を書いていたので,`proconio` で読んだ標準入力を用いて,条件分岐が実行時に起こることを説明しました. 34 | 1. ブロックとスコープ. 35 | `if` 式でブロックを目にしたので,変数のスコープや,値を返すブロックについても知ることができます. 36 | 1. アサートとパニック. 37 | 配列を学ぶとき,範囲外アクセスの挙動を理解するのに必要です.副産物として,`assert!` や `assert_eq!` を含むサンプルコードが読めるようになります. 38 | 1. タプルと配列. 39 | `let` におけるパターンマッチもここで知っておくと,後で楽でしょう. 40 | 1. フォーマット出力. 41 | `println!` マクロの `{:x}` や `{2:3.1}` などです.タプルと配列が上で登場したので,`{:?}` についても知ることができます. 42 | 1. 参照とライフタイム. 43 | まだ `mut` 変数 / `mut` 参照を扱わないため,そこまで難しくないはずです.**参照のライフタイムが元の変数のスコープを越えられない**ことだけ,しっかりと押さえましょう.上でフォーマット出力を扱ったのは,ここで `{:p}` を使いアドレスを見て,イメージを掴むためです. 44 | 1. `for` 式. 45 | 配列と参照を扱ったので,配列への参照を `for` 式に渡して走査することを学べます. 46 | 1. `mut` 変数. 47 | `for` 式の中で変数を上書きしたいという需要が発生するので,ついに `mut` 変数が必要になります.`+=` のような複合代入演算子もここで知ることができます. 48 | 1. `mut` 参照. 49 | **shared XOR mutable** を理解します.ただし,この段階ではその有難みまで感じ取るのは難しいでしょう.ひとまず,そういうルールとして受け入れるしかないのかもしれないと考えています. 50 | 1. 関数. 51 | 既に値を返すブロックを扱っているため,値を返す関数も導入しやすいでしょう. 52 | 1. 再帰関数. 53 | **実は我々はまだヒープを必要としない**ので,余計なことを考えずにスタックの仕組みを理解できます. 54 | 1. ベクタ. 55 | 書けるプログラムの幅が一気に広がります. 56 | 1. ヒープと所有権. 57 | スタックの仕組みを理解していれば,それだけでベクタを実現しようとするとだいぶ困ってしまうということが分かると思います.そこで動的なアロケートとデアロケートを知り,**所有権**に基づいた適切なドロップの仕組みを知ることができます.ムーブによる所有権の移動も分かります.既に学んだ借用検査のルールと合わせることで,自然にコンパイルが通るプログラムを書く力が身に付きます. 58 | 1. 参照渡し. 59 | 既に学んだことの組合せでしかないので,何も難しくありません.頻繁に使う手法として押さえておけば十分です. 60 | 61 | この順序なら,誰でも Rust の基礎をしっかり身に付けることができるでしょう. 62 | 63 | Rust 学習の道のりは,今どんどん整備されています.誰もが「初めてのプログラミング言語」として当たり前に Rust を選択できる時代も,そう遠くなさそうです. 64 | -------------------------------------------------------------------------------- /articles/rust-graph-trait.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rust で抽象化したグラフを扱う" 3 | emoji: "🪩" 4 | type: "tech" # tech: 技術記事 / idea: アイデア 5 | topics: ["rust"] 6 | published: true 7 | --- 8 | 9 | メモ程度の軽いやつです. 10 | 11 | # 抽象化 12 | 有向グラフの隣接リストの「与えられた頂点から直接辺が伸びている頂点を列挙できる」という機能をトレイトとして抜き出し,辺を陽に持たないグリッドグラフなどにも impl したいねという話です. 13 | 14 | まずコード全部出したあと下で説明します 15 | ```rust 16 | mod graph { 17 | use std::{iter, slice}; 18 | 19 | pub trait Graph<'a> { 20 | type Node; 21 | type Iter: Iterator; 22 | fn next(&'a self, _: Self::Node) -> Self::Iter; 23 | } 24 | 25 | pub struct AdjList(Vec>); 26 | impl AdjList { 27 | pub fn new(n: usize) -> AdjList { 28 | AdjList(vec![Vec::new(); n]) 29 | } 30 | pub fn add_directed(&mut self, from: usize, to: usize) { 31 | self.0[from].push(to); 32 | } 33 | pub fn add_undirected(&mut self, u: usize, v: usize) { 34 | self.add_directed(u, v); 35 | self.add_directed(v, u); 36 | } 37 | } 38 | impl<'a> Graph<'a> for AdjList { 39 | type Node = usize; 40 | type Iter = iter::Cloned>; 41 | fn next(&'a self, node: usize) -> Self::Iter { 42 | self.0[node].iter().cloned() 43 | } 44 | } 45 | 46 | pub struct Grid4 { 47 | height: usize, 48 | width: usize, 49 | } 50 | impl Grid4 { 51 | pub fn new(height: usize, width: usize) -> Grid4 { 52 | Grid4 { height, width } 53 | } 54 | } 55 | pub const DIR4: [(usize, usize); 4] = [(1, 0), (0, 1), (!0, 0), (0, !0)]; 56 | pub struct Grid4Iter<'a> { 57 | r: usize, 58 | c: usize, 59 | grid: &'a Grid4, 60 | iter: std::slice::Iter<'static, (usize, usize)>, 61 | } 62 | impl<'a> Iterator for Grid4Iter<'a> { 63 | type Item = (usize, usize); 64 | fn next(&mut self) -> Option { 65 | for &(i, j) in &mut self.iter { 66 | let r = self.r.wrapping_add(i); 67 | let c = self.c.wrapping_add(j); 68 | if r < self.grid.height && c < self.grid.width { 69 | return Some((r, c)); 70 | } 71 | } 72 | None 73 | } 74 | } 75 | impl<'a> Graph<'a> for Grid4 { 76 | type Node = (usize, usize); 77 | type Iter = Grid4Iter<'a>; 78 | fn next(&'a self, (r, c): (usize, usize)) -> Grid4Iter<'a> { 79 | Grid4Iter { 80 | r, 81 | c, 82 | grid: self, 83 | iter: DIR4.iter(), 84 | } 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | # トレイト 91 | 重みなしの場合 92 | ```rust 93 | pub trait Graph<'a> { 94 | type Node; 95 | type Iter: Iterator; 96 | fn next(&'a self, _: Self::Node) -> Self::Iter; 97 | } 98 | ``` 99 | `Node` は頂点を表す型です.隣接リストなら `usize`,グリッドなら `(usize, usize)` みたいな. `next` が次の頂点を列挙する関数です.イテレータを返すので,その型も `Iter` として与えます. 100 | 101 | # 隣接リスト 102 | これを隣接リスト 103 | ```rust 104 | pub struct AdjList(Vec>); 105 | ``` 106 | に impl すると, 107 | ```rust 108 | use std::{iter, slice}; 109 | 110 | impl<'a> Graph<'a> for AdjList { 111 | type Node = usize; 112 | type Iter = iter::Cloned>; 113 | fn next(&'a self, node: usize) -> Self::Iter { 114 | self.0[node].iter().cloned() 115 | } 116 | } 117 | ``` 118 | となります. 119 | 120 | # グリッド 121 | 4 方向のグリッドグラフ 122 | ```rust 123 | pub struct Grid4 { 124 | height: usize, 125 | width: usize, 126 | } 127 | ``` 128 | に impl する場合, 129 | ```rust 130 | pub const DIR4: [(usize, usize); 4] = [(1, 0), (0, 1), (!0, 0), (0, !0)]; 131 | pub struct Grid4Iter<'a> { 132 | r: usize, 133 | c: usize, 134 | grid: &'a Grid4, 135 | iter: std::slice::Iter<'static, (usize, usize)>, 136 | } 137 | ``` 138 | を使って 139 | ```rust 140 | impl<'a> Graph<'a> for Grid4 { 141 | type Node = (usize, usize); 142 | type Iter = Grid4Iter<'a>; 143 | fn next(&'a self, (r, c): (usize, usize)) -> Grid4Iter<'a> { 144 | Grid4Iter { 145 | r, 146 | c, 147 | grid: self, 148 | iter: DIR4.iter(), 149 | } 150 | } 151 | } 152 | 153 | impl<'a> Iterator for Grid4Iter<'a> { 154 | type Item = (usize, usize); 155 | fn next(&mut self) -> Option { 156 | for &(i, j) in &mut self.iter { 157 | let r = self.r.wrapping_add(i); 158 | let c = self.c.wrapping_add(j); 159 | if r < self.grid.height && c < self.grid.width { 160 | return Some((r, c)); 161 | } 162 | } 163 | None 164 | } 165 | } 166 | ``` 167 | といった感じかな…… 168 | 169 | `DIR4` を `pub` にしているのは,何かに使うかも〜という気持ちです. 170 | 171 | わざわざ `Grid4Iter` に `<'a>` を付けて中で `&'a Grid4` を持ってるのは, `Grid4Iter` のライフタイムが元の `Grid4` を超えるようなコードはまずそうな感じがするからです. -------------------------------------------------------------------------------- /articles/rust-pitfalls.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rustの誤解と改善" 3 | emoji: "🙌" 4 | type: "tech" # tech: 技術記事 / idea: アイデア 5 | topics: ["rust"] 6 | published: true 7 | --- 8 | 9 | ChatGPT にこんな要求をしました。 10 | 11 | > Rust自体は難しい言語ではなく、むしろ単純な機能の組み合わせで豊かな表現力を提供しています。なのに、他言語の複雑な機能をRustで再現しようとして混乱する人が跡を絶たず、いつの間にかRustは難しいという誤解が定着してしまいました。そこで、他言語の考え方をRustに持ち込んで失敗した例をいくつか挙げ、改善案を教えて下さい。 12 | 13 | 本記事は ChatGPT の出力をまとめたものです。 14 | 15 | # GPT-4o 16 | 17 | 色々試したのですが、まず2025年2月18日時点のGPT-4oから。 18 | 19 | > 確かに、Rustの設計はシンプルな概念の組み合わせですが、他言語の習慣を持ち込むことで混乱が生じることが多いですね。いくつか典型的な失敗例を挙げ、それぞれの改善案を考えてみましょう。 20 | 21 | よろしくお願いします。 22 | 23 | ## クラスベースのオブジェクト指向をそのままRustに適用しようとする 24 | 25 | > **失敗例:** C++やJavaのようにクラスを作りたがり、すべてを `struct` + `impl` でカプセル化しようとする。特に、継承を再現しようとして無理にトレイトを多用する。 26 | 27 | と述べた後、コード例とともに以下の問題点を挙げています。 28 | 29 | > **問題点:** 30 | > - 継承がないRustでオブジェクト指向を無理に模倣すると、動的ディスパッチ(`dyn`)を乱用してパフォーマンスが悪化する。 31 | > - Rustの所有権システムと組み合わせるとライフタイムの扱いが難しくなる。 32 | > - 単純な列挙型や `match` を使った方がシンプルになることが多い。 33 | 34 | 伝統的なオブジェクト指向のやり方ですね。そもそも C++ でポリモーフィズムを書くとき「仮想関数テーブル」の存在を認識しているでしょうか。何もかも継承で書くと無駄に関数ポインタが増えていきます。Tagged union で済むなら、その方がよっぽどシンプルです。 35 | 36 | 普段からコンパイル後の視点を意識しておけばよかった話ですね。 37 | 38 | ## 例外ベースのエラーハンドリングを持ち込む 39 | 40 | > **失敗例:** 他の言語のように `try/catch` の感覚で `panic!()` を使いまくる。 41 | 42 | > **問題点:** 43 | > - Rustはパニックを回復不能な状態(バグや致命的エラー)とみなしている。 44 | > - `panic!()` を多用すると、スレッドがクラッシュするため安全でない。 45 | > - `Result` を使った方が明示的で安全。 46 | 47 | もしかして `catch_unwind` しまくる人ですか? 48 | 49 | エラー処理の方法は言語によって違うので、1 つの考え方にとらわれていると他で困るのは当然ですね。言いたいことは分かりますが、`Result` と `panic!` の違いは「安全」「安全でない」ではないので表現は若干ズレています。 50 | 51 | ## すべてをミュータブルにしようとする 52 | 53 | > **失敗例:** C++やPythonのように、何でも `mut` をつけて変更しようとする。 54 | 55 | > **問題点:** 56 | > - Rustではミュータブルな参照は同時に1つしか持てないため、所有権の問題が発生しやすい。 57 | > - 変更が不要なら、`mut` をなくすことで安全性が向上する。 58 | 59 | いわゆる手続き型と関数型の違いですね。C 系に慣れてると、とりあえず変数を宣言しておいて、後から値に手を加えまくる発想になりやすい。別に「Rust vs. 他言語」という構図ではないので、別の考え方にも慣れろとしか言いようがありませんね。 60 | 61 | ## **グローバル変数を多用する** 62 | 63 | > **失敗例:** CやPythonのように、グローバルな `static mut` 変数を多用する。 64 | 65 | > **問題点:** 66 | > - `static mut` はスレッドセーフではなく、`unsafe` を使わないと変更できない。 67 | > - スレッド間でデータ競合が発生しやすい。 68 | 69 | > **改善案:** スレッドセーフな `Mutex` や `Atomic` を使う。 70 | 71 | これもありますね。ただ最後の改善案が若干気に入りません。おそらく、CやPythonの気持ちでグローバル変数を使おうとしている多くの状況では、`&mut self` をとるメソッドをもつ構造体を 1 つ持っておけばいいんじゃないでしょうか?たとえば string interning ライブラリを提供するなら、`StringInterner` 構造体に `new` や `intern` / `get` メソッドをもたせておけばいいわけです。これを `Mutex` で強引に解決するのは、それこそ他言語の考え方を持ち込んでいるに過ぎません。 72 | 73 | まずは、より簡単な機能の組み合わせで済むか考えるべきですね。 74 | 75 | # o3-mini 76 | 77 | 次は o3-mini です。 78 | 79 | > Rustはシンプルな機能の組み合わせで表現力を発揮する一方、他言語の「当たり前」をそのまま移植しようとすると、かえってRustの意図する設計思想や仕組みと食い違い、混乱を招くことがあります。以下に、いくつかの「他言語の考え方を持ち込んで失敗した例」と、その改善策を挙げます。 80 | 81 | よろしくお願いします。 82 | 83 | ## **Null の概念のそのままの移植** 84 | 85 | > - **失敗例:** 86 | > JavaやC/C++でおなじみの「null」を、Rustでも扱いたいと考え、null相当の概念を無理に実装しようとする。 87 | > → 結果として、実行時の不具合や安全性の低下を招く。 88 | > 89 | > - **改善策:** 90 | > Rustは「null」を排除し、**`Option`** 型で「値がある/ない」を明示的に扱います。 91 | > → `Option` を利用し、`match` や `if let` でパターンマッチングすることで、コンパイル時に漏れを防ぐ安全なコードを書く。 92 | 93 | 「null相当の概念を無理に実装」って何でしょうね。`unsafe` でヌルポインタ?どうしたらそんな発想に…… 94 | 95 | 自分の観測範囲だと `Option` はすんなり受け入れている人が多かったので、あまり共感する部分がありません。 96 | 97 | ## **クラシックな継承モデルの再現** 98 | 99 | > - **失敗例:** 100 | > JavaやC++でのクラス継承や仮想関数の考え方をRustにそのまま適用し、「スーパークラス・サブクラス」の関係を無理に再現しようとする。 101 | > → 結果として、Rustでは存在しない継承の概念に固執し、設計が複雑化する。 102 | > 103 | > - **改善策:** 104 | > Rustは**継承ではなく「トレイト」と「コンポジション」**による設計を推奨しています。 105 | > → オブジェクト指向の多態性を実現したい場合は、トレイトによるインターフェース定義や、ジェネリクスを活用する。これにより、柔軟かつコンパイル時に安全な設計が可能になる。 106 | 107 | 「コンポジション」の時点でオブジェクト指向にとらわれてないか? 108 | 109 | Rust に OOP を持ち込もうとする話は 4o の回答と共通していますね。 110 | 111 | ## **例外処理の概念の押し付け** 112 | 113 | > - **失敗例:** 114 | > PythonやJavaの例外処理に慣れていると、Rustでも同様に例外(`try/catch` に相当する機構)を期待して、エラーが発生したら例外を投げる実装を試みる。 115 | > → 実際、Rustには例外機構がなく、パニック(`panic!`)は基本的に致命的なエラーに使うため、通常のエラーハンドリングとしては適さない。 116 | > 117 | > - **改善策:** 118 | > Rustでは、**`Result`** 型を利用してエラーを明示的に扱います。 119 | > → `match` や `?` 演算子を使ってエラー伝播を行い、例外ではなく明示的なエラー処理の流れを設計する。これにより、エラーが発生した場合の挙動が明確になり、予期せぬクラッシュを防止できる。 120 | 121 | これはさっきの 4o よりまともなことを言っていそうですね。 122 | 123 | ちなみに今は C++ にも `std::expected` があります。 124 | 125 | ## **所有権・借用ルールへの無理解** 126 | 127 | > - **失敗例:** 128 | > 他言語では自由に変数を変更できるため、「自由なミュータビリティ」を前提にRustでも同じような書き方をしてしまい、借用ルール違反でコンパイラエラーが発生する。 129 | > → 結果として、なぜエラーになるのか理解できず、コードが書けなくなる。 130 | > 131 | > - **改善策:** 132 | > Rustの**所有権・借用モデル**は、安全性を確保するための根幹です。 133 | > → まずは、所有権、借用、ライフタイムの基本概念を学習し、**コンパイラのエラーメッセージをヒント**に正しい設計を模索すること。例えば、必要な場面では`&mut`を適切に使い、不要なコピーを避けることで、意図した動作を実現できる。 134 | 135 | そもそも、そんな書き方は他言語でも避けるべきですね。「自由なミュータビリティ」という言葉が怖すぎる、そんな人はマルチスレッドで全てをめちゃくちゃにしていそう。 136 | 137 | 「❌ Rust が難しい」「⭕ プログラミングが難しい」と言われる理由の 1 つだと思います。 138 | 139 | ## **マクロの過剰利用** 140 | 141 | > - **失敗例:** 142 | > Lisp系言語などでマクロを多用していた開発者が、Rustでも同じように複雑なマクロを組み立て、コードの抽象化を図ろうとする。 143 | > → しかし、過度なマクロ利用は可読性を損ない、デバッグも困難になる。 144 | > 145 | > - **改善策:** 146 | > Rustのマクロは、**コードの繰り返しを減らすための補助ツール**として有効ですが、基本的にはジェネリクスや関数、トレイトなどの標準機能を優先する。 147 | > → まずはシンプルな構造で実装し、本当にマクロが必要な箇所だけに限定して使うことで、保守性の高いコードを書くことができる。 148 | 149 | Lisp 系を知らないため状況が想像できないのですが、そんなことがあるんですか……? 150 | 151 | Rust のマクロは C / C++ に比べてマシですが、個人的にはまだ不満があります。今後良くなるといいですね。 152 | 153 | # GPT-4o mini 154 | 155 | 最後は 4o mini にも聞いてみます。 156 | 157 | > Rustが難しいと感じられがちな理由の一つは、他の言語から来た人々がRust独自の設計哲学や特徴を理解せずに、既存の慣習を持ち込んでしまうことです。Rustは多くの点で簡潔で直感的ですが、他の言語に慣れていると、そのシンプルさが逆に難しさに感じられることがあります。以下は、他の言語の考え方をRustに持ち込んで失敗しがちな例と、その改善案です。 158 | 159 | よろしくお願いします。 160 | 161 | ## **メモリ管理の誤解:** 162 | 163 | > **失敗例:** 164 | > 他の言語ではガーベジコレクションが提供されているため、メモリ管理にあまり気を使わずに済みます。しかし、Rustでは所有権(Ownership)と借用(Borrowing)の概念が厳格に適用され、手動でメモリ管理をしないといけません。これを理解せずに、Rustのメモリ管理が「面倒だ」と感じてしまうことがあります。 165 | > 166 | > **改善案:** 167 | > Rustの所有権と借用のルールは、プログラムが効率的に動作するために重要な部分です。このルールは、メモリリークやデータ競合のリスクを排除し、パフォーマンスの向上をもたらします。まずは、所有権の概念を十分に理解し、コード内でどの変数が所有権を持つか、どの変数が借用しているかを把握することが重要です。コンパイラがエラーを出すのは、Rustがコードの安全性を保証するためなので、エラーメッセージをよく読んで理解することがポイントです。 168 | 169 | そういう理由でそもそも Rust が適していない場面というのはありますよね。GC のある言語と無い言語、どちらが良い悪いではなく、適切な選択が重要ですね。 170 | 171 | ## **ポインタと参照の混乱:** 172 | 173 | > **失敗例:** 174 | > 他の言語でポインタを使って操作していた経験から、Rustのポインタ(参照)を正しく使えないことがあります。例えば、参照と可変参照を混同してしまったり、ポインタを使う感覚で借用の範囲を超えてアクセスしてしまうことがあります。 175 | > 176 | > **改善案:** 177 | > Rustの参照には「不変参照(immutable reference)」と「可変参照(mutable reference)」があります。それぞれの違いを理解し、同時に両方を使わないようにすることが重要です。可変参照は一度に1つだけ持つことができ、他の変数がその値を借用することはできません。この点を理解することで、Rust特有のメモリ安全性を保ちながら、意図した通りにコードを書くことができます。 178 | 179 | 「ポインタを使う感覚で借用の範囲を超えてアクセス」、そもそもが良くない例ですね。C / C++ ならきっと未定義動作です。 180 | 181 | ## **型推論の誤解:** 182 | 183 | > **失敗例:** 184 | > Rustの型推論は強力ですが、他言語の習慣で型を明示的に指定することに慣れていると、型推論がどこまで働くかがわからず、型エラーを引き起こしてしまうことがあります。また、Rustでは型の推論がコンパイラに負担をかけない範囲で行われるため、最適な型を選ぶことが重要です。 185 | > 186 | > **改善案:** 187 | > 型推論を信頼することは大事ですが、コンパイラのエラーメッセージをよく読んで、必要な場合は明示的に型を指定してあげることが解決策になります。特に、ライフタイム(Lifetime)やトレイト(Trait)を扱う際には、明示的な型指定が有効です。 188 | 189 | あ〜〜〜 `` can't call method `...` on ambiguous numeric type `{integer}` `` とか。Rust の場合だと完全な型推論は無理なので、難しいですね。 190 | 191 | ## **エラーハンドリングの混乱(`Result`と`Option`):** 192 | 193 | > **失敗例:** 194 | > 他の言語で例外処理を使い慣れている場合、Rustの`Result`や`Option`のエラーハンドリングに戸惑うことがあります。Rustでは、エラーや値の無効状態を明示的に扱うことが要求されるため、`Result`や`Option`を使わずにエラーを無視したり、予期しない値を受け入れたりしてしまう場合があります。 195 | > 196 | > **改善案:** 197 | > Rustのエラーハンドリングは、エラーが発生した場合でもプログラムを安全に終了させるための仕組みとして重要です。`Result`型や`Option`型を使いこなし、パターンマッチングでエラーを適切に処理することを学びましょう。特に`unwrap()`や`expect()`の使い方には注意が必要で、これらを使うのは本当に安全であると確認できた場合のみです。 198 | 199 | はい。 200 | 201 | ## **関数型プログラミング(FP)との違い:** 202 | 203 | > **失敗例:** 204 | > Rustは一部の関数型プログラミングの特徴を持っていますが、完全なFP言語ではありません。例えば、他の言語のように高階関数やラムダ式を頻繁に使ってしまい、Rustの特徴的な書き方(例えば、`for`ループや`match`式)を避けてしまうことがあります。 205 | > 206 | > **改善案:** 207 | > Rustの関数型プログラミングの特徴(`map`、`filter`、`fold`など)を活かしつつ、Rustらしい書き方を理解しましょう。Rustは、構造体や列挙型(`enum`)といった強力な型システムを使って、コードの表現力を高めることができます。関数型の考え方を使う際も、Rustらしい型安全性やパターンマッチングを活かすことが大切です。 208 | 209 | うわ、思想だ!逃げろ逃げろ。 210 | 211 | Clippy に何も言われなければ何だっていいと思います。 212 | 213 | # まとめ 214 | 215 | 細かい部分は微妙ですが、とりあえず色々挙げてくれてよかったですね。 216 | 217 | オブジェクト指向の発想、手続き型の発想、グローバル変数の利用など、他言語に慣れてしまっているからこそ失敗する場面が多くあります。新しいことを始めるならまず基本に立ち返るのが大切ですね。 218 | -------------------------------------------------------------------------------- /articles/rust-transpose.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Rust で,行と列を転置する" 3 | emoji: "🔄" 4 | type: "tech" # tech: 技術記事 / idea: アイデア 5 | topics: ["Rust", "競技プログラミング"] 6 | published: true 7 | --- 8 | 9 | :::message alert 10 | この記事中のコードより[こっち](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&code=fn%20main()%20%7B%0A%20%20%20%20let%20matrix%20%3D%20vec!%5Bvec!%5B1%2C%202%2C%203%5D%2C%20vec!%5B4%2C%205%2C%206%5D%2C%20vec!%5B7%2C%208%2C%209%5D%5D%3B%0A%20%20%20%20let%20result%3A%20Vec%3C_%3E%20%3D%20matrix.into_iter().transpose().collect()%3B%0A%20%20%20%20assert_eq!(result%2C%20vec!%5Bvec!%5B1%2C%204%2C%207%5D%2C%20vec!%5B2%2C%205%2C%208%5D%2C%20vec!%5B3%2C%206%2C%209%5D%5D)%3B%0A%7D%0A%0Atrait%20Transpose%3CIter%3A%20IntoIterator%3E%20%7B%0A%20%20%20%20fn%20transpose(self)%20-%3E%20Transposed%3CIter%3E%3B%0A%7D%0A%0Aimpl%3CT%3E%20Transpose%3CT%3A%3AItem%3E%20for%20T%0Awhere%0A%20%20%20%20T%3A%20IntoIterator%2C%0A%20%20%20%20T%3A%3AItem%3A%20IntoIterator%2C%0A%7B%0A%20%20%20%20fn%20transpose(self)%20-%3E%20Transposed%3CT%3A%3AItem%3E%20%7B%0A%20%20%20%20%20%20%20%20Transposed(self.into_iter().map(IntoIterator%3A%3Ainto_iter).collect())%0A%20%20%20%20%7D%0A%7D%0A%0Astruct%20Transposed%3CIter%3A%20IntoIterator%3E(Vec%3CIter%3A%3AIntoIter%3E)%3B%0A%0Aimpl%3CIter%3A%20IntoIterator%3E%20Iterator%20for%20Transposed%3CIter%3E%20%7B%0A%20%20%20%20type%20Item%20%3D%20Vec%3CIter%3A%3AItem%3E%3B%0A%20%20%20%20fn%20next(%26mut%20self)%20-%3E%20Option%3CSelf%3A%3AItem%3E%20%7B%0A%20%20%20%20%20%20%20%20self.0.iter_mut().map(Iterator%3A%3Anext).collect()%0A%20%20%20%20%7D%0A%7D%0A)の方がすっきりしていて普通です.いずれ直します. 11 | ::: 12 | 13 | # やりたいこと 14 | 15 | ``` 16 | [[1, 2, 3], 17 | [4, 5, 6], 18 | [7, 8, 9]] 19 | ``` 20 | という 2 次元ベクタがあったときに,これを転置して 21 | ``` 22 | [[1, 4, 7], 23 | [2, 5, 8], 24 | [3, 6, 9]] 25 | ``` 26 | にする `transpose` 関数を作りたいと思います. `Rust` はイテレータが扱いやすいので,転置したもののイテレータが得られるようにします.使い方としては,次のようになります. 27 | 28 | ```rust 29 | fn main() { 30 | let matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; 31 | 32 | let result: Vec<_> = matrix.iter().transpose().collect(); 33 | 34 | assert_eq!( 35 | result, 36 | vec![vec![&1, &4, &7], vec![&2, &5, &8], vec![&3, &6, &9]] 37 | ); 38 | } 39 | ``` 40 | 41 | `transpose` 関数はベクタへのイテレータを返すようにします.たとえば今回なら,返り値は `Iterator>` を impl しており,それを `.collect()` した `result` の型は `Vec>` となっています. 42 | 43 | # 発想 44 | 返り値を構造体 `Transposed` として, `impl Iterator for Transposed` することにします. 45 | 46 | `Transposed` は `matrix[0].iter()`, `matrix[1].iter()`, `matrix[2].iter()`, ……をベクタとして持っておき, `.next()` が呼び出されるたびに各イテレータを一つずつ進めます. 47 | 48 | よって `Transposed` のもつメンバはイテレータを要素にもつベクタ 1 つです.これの名前を `iters` とします. 49 | 50 | # 境界 51 | 各 `matrix[i]` についてイテレータが呼び出せればよいです. `&Vec` は `IntoIterator` を impl しているので,これを利用します. 52 | 53 | よって, `&matrix[0]` `&matrix[1]` `&matrix[2]` ……が得られれば,各々に `IntoIterator::into_iter` を適用することでベクタ `iters` を作ることができます. 54 | 55 | また, `&Vec>` は `IntoIterator>` を impl しているので, `&matrix` に対し `IntoIterator::into_iter` を呼び出せば `&matrix[0]` `&matrix[1]` `&matrix[2]` ……が得られます. 56 | 57 | よって, `Iter: IntoIterator` , `T: IntoIterator` として `T` に対して `transpose` 関数を呼び出せるように設計します. 58 | 59 | # 実装 60 | ## トレイト 61 | `x.transpose()` の形式で呼び出したいので, `trait Transpose` を定義します. 62 | ```rust 63 | trait Transpose<'a, Elem, Iter, T> 64 | where 65 | Elem: 'a, 66 | Iter: IntoIterator, 67 | T: IntoIterator, 68 | { 69 | fn transpose(self) /* -> Transposed 構造体 */; 70 | } 71 | 72 | impl<'a, Elem, Iter, T> Transpose<'a, Elem, Iter, T> for T 73 | where 74 | Elem: 'a, 75 | Iter: IntoIterator, 76 | T: IntoIterator, 77 | { 78 | fn transpose(self) /* -> Transposed 構造体 */ { 79 | todo!(); 80 | } 81 | } 82 | ``` 83 | 84 | `IntoIterator::into_iter` 関数の返り値は `IntoIterator::IntoIter` です. `Transposed` 構造体は, `IntoIterator::IntoIter` を要素にもつベクタ `iters` をメンバとしてもつので, `Transposed` は型パラメータとして `Elem` と `Iter: IntoIterator` をとります. 85 | ```rust 86 | struct Transposed<'a, Elem, Iter> 87 | where 88 | Elem: 'a, 89 | Iter: IntoIterator, 90 | { 91 | iters: Vec, 92 | } 93 | ``` 94 | 95 | よって,先ほど書かなかった `transpose` 関数の返り値の型は, `Transposed<'a, Elem, Iter>` となります. 96 | 97 | そして,この `Transposed<'a, Elem, Iter>` に `Iterator>` を impl します. 98 | ```rust 99 | impl<'a, Elem, Iter> Iterator for Transposed<'a, Elem, Iter> 100 | where 101 | Elem: 'a, 102 | Iter: IntoIterator, 103 | { 104 | type Item = Vec<&'a Elem>; 105 | fn next(&mut self) -> Option { 106 | todo!(); 107 | } 108 | } 109 | ``` 110 | ## 関数の中身 111 | 実装する関数は, `T` 上の `Transpose::transpose` と, `Transposed` 上の `Iterator::next` の 2 つです. 112 | 113 | `transpose` 関数は, `T` に対し `into_iter()` を呼び出し, `IntoIterator::into_iter` を `map` した上で `Vec` に `collect` します.返り値は,これを `iters` メンバにもつ `Transposed` です.よって次のようになります. 114 | ```rust 115 | impl<'a, Elem, Iter, T> Transpose<'a, Elem, Iter, T> for T 116 | where 117 | Elem: 'a, 118 | Iter: IntoIterator, 119 | T: IntoIterator, 120 | { 121 | fn transpose(self) -> Transposed<'a, Elem, Iter> { 122 | Transposed { 123 | iters: self.into_iter().map(IntoIterator::into_iter).collect(), 124 | } 125 | } 126 | } 127 | ``` 128 | 129 | `next` 関数は,まず `iters` の各要素に対し `Iterator::next` を `map` します.すると, `Iterator::next` が `Option<&Elem>` を返すので,これを `Option>` に `collect` します( `V: FromIterator` なる `V` と `A` について, `FromIterator> for Option` が impl されています.今回は `A` を `&Elem` , `V` を `Vec<&Elem>` とした形です).よって次のようになります. 130 | ```rust 131 | impl<'a, Elem, Iter> Iterator for Transposed<'a, Elem, Iter> 132 | where 133 | Elem: 'a, 134 | Iter: IntoIterator, 135 | { 136 | type Item = Vec<&'a Elem>; 137 | fn next(&mut self) -> Option { 138 | self.iters.iter_mut().map(Iterator::next).collect() 139 | } 140 | } 141 | ``` 142 | # 全体 143 | 全体では,次のようになります. 144 | ```rust 145 | fn main() { 146 | let matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; 147 | 148 | let result: Vec<_> = matrix.iter().transpose().collect(); 149 | 150 | assert_eq!( 151 | result, 152 | vec![vec![&1, &4, &7], vec![&2, &5, &8], vec![&3, &6, &9]] 153 | ); 154 | } 155 | 156 | trait Transpose<'a, Elem, Iter, T> 157 | where 158 | Elem: 'a, 159 | Iter: IntoIterator, 160 | T: IntoIterator, 161 | { 162 | fn transpose(self) -> Transposed<'a, Elem, Iter>; 163 | } 164 | 165 | impl<'a, Elem, Iter, T> Transpose<'a, Elem, Iter, T> for T 166 | where 167 | Elem: 'a, 168 | Iter: IntoIterator, 169 | T: IntoIterator, 170 | { 171 | fn transpose(self) -> Transposed<'a, Elem, Iter> { 172 | Transposed { 173 | iters: self.into_iter().map(IntoIterator::into_iter).collect(), 174 | } 175 | } 176 | } 177 | 178 | struct Transposed<'a, Elem, Iter> 179 | where 180 | Elem: 'a, 181 | Iter: IntoIterator, 182 | { 183 | iters: Vec, 184 | } 185 | 186 | impl<'a, Elem, Iter> Iterator for Transposed<'a, Elem, Iter> 187 | where 188 | Elem: 'a, 189 | Iter: IntoIterator, 190 | { 191 | type Item = Vec<&'a Elem>; 192 | fn next(&mut self) -> Option { 193 | self.iters.iter_mut().map(Iterator::next).collect() 194 | } 195 | } 196 | ``` 197 | [Playground のリンク](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=45a599e006505df85317fd43bfd97388). 198 | 199 | また,これを使うと [ABC182 E - Akari](https://atcoder.jp/contests/abc182/tasks/abc182_e) が次のように解けます: [AC コード](https://atcoder.jp/contests/abc182/submissions/20202805). 200 | 201 | 以上,行と列の転置でした.この記事が誰かの役に立てば幸いです. 202 | -------------------------------------------------------------------------------- /articles/satanic-fourier.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ビット演算を用いた非再帰 FFT" 3 | emoji: "🔀" 4 | type: "tech" 5 | topics: ["競プロ", "FFT"] 6 | published: true 7 | --- 8 | 9 | # 離散フーリエ変換( DFT )の定義 10 | $n$ 個の複素数列 $\bm{x} = (x_0, x_1, \ldots, x_{n - 1})$ に対し,これらの**離散フーリエ変換**( DFT ) $\bm{X} = (X_0, X_1, \ldots, X_{n - 1})$ は次で定義されます. 11 | 12 | $$ 13 | X_k = \sum_{j = 0}^{n - 1} \zeta_n^{-jk} x_j 14 | \tag{1} 15 | $$ 16 | 17 | ただし $\zeta_n$ は 18 | 19 | $$ 20 | \zeta_n = e^{\frac{2\pi i}{n}} = \cos\frac{2\pi}{n} + i\sin\frac{2\pi}{n} 21 | $$ 22 | 23 | で定義され, $n$ 乗すると $1$ になる虚数の 1 つです. 24 | 25 | $\zeta_{2n}$ の $2a$ 乗を計算すると, $\zeta_{2n}^{2a} = e^{\frac{2\pi i}{2n}\cdot 2a} = e^{\frac{2\pi i}{n} \cdot a}$ となって, $\zeta_n$ の $a$ 乗 $\zeta_n^a$ と同じになります. 26 | 27 | また, DFT の定義式 $(1)$ は,整数 $k$ が $0 \leq k < n$ の範囲になくても計算することができます. $\zeta_n^{a + n} = \zeta_n^a \cdot \zeta_n^n = \zeta_n^a$ に注意すると, $X_{k + n} = X_k$ が成り立つことが分かります.よって, $k$ が $0 \leq k < n$ の範囲を超えたときは, $k$ の代わりに, $k$ を $n$ で割った余りで考えても問題ありません. 28 | 29 | # 時間間引き( DIT ) 30 | $n$ が偶数であると仮定し, $n = 2m$ とおきます.このとき,長さ $m$ の数列の DFT を用いて長さ $n$ の数列の DFT を表すことを考えます. 31 | 32 | まず,定義 $(1)$ より長さ $n$ の DFT は 33 | 34 | $$ 35 | \begin{aligned} 36 | X_k &= \sum_{j = 0}^{n - 1} \zeta_n^{-jk} x_j \\ 37 | &= x_0 + \zeta_n^{-k} x_1 + \zeta_n^{-2k} x_2 + \cdots + \zeta_n^{-(n - 1)k} x_{n - 1} 38 | \end{aligned} 39 | $$ 40 | 41 | ですが,ここで右辺を $j$ ( $x$ の添字)の偶奇で 2 つに分けます.すると, 42 | 43 | $$ 44 | \begin{aligned} 45 | X_k &= x_0 + \zeta_n^{-2k} x_2 + \zeta_n^{-4k} x_4 + \cdots + \zeta_n^{-(n - 2)k} x_{n - 2} \\ 46 | &+ \zeta_n^{-k} x_1 + \zeta_n^{-3k} x_3 + \zeta_n^{-5k} x_5 + \cdots + \zeta_n^{-(n - 1)k} x_{n - 1} 47 | \end{aligned} 48 | $$ 49 | 50 | となります(上が偶数,下が奇数).奇数の方は $\zeta_n^{-k}$ でくくることができます. 51 | 52 | $$ 53 | \begin{aligned} 54 | X_k &= x_0 + \zeta_n^{-2k} x_2 + \zeta_n^{-4k} x_4 + \cdots + \zeta_n^{-(n - 2)k} x_{n - 2} \\ 55 | &+ \zeta_n^{-k} (x_1 + \zeta_n^{-2k} x_3 + \zeta_n^{-4k} x_5 + \cdots + \zeta_n^{-(n - 2)k} x_{n - 1}) 56 | \end{aligned} 57 | $$ 58 | 59 | さらに, $\zeta$ の添字と肩にある $n$ に $n = 2m$ を代入すると 60 | 61 | $$ 62 | \begin{aligned} 63 | X_k &= x_0 + \zeta_{2m}^{-2k} x_2 + \zeta_{2m}^{-4k} x_4 + \cdots + \zeta_{2m}^{-2(m - 1)k} x_{n - 2} \\ 64 | &+ \zeta_n^{-k} (x_1 + \zeta_{2m}^{-2k} x_3 + \zeta_{2m}^{-4k} x_5 + \cdots + \zeta_{2m}^{-2(m - 1)k} x_{n - 1}) 65 | \end{aligned} 66 | $$ 67 | 68 | となりますが, $\zeta_{2m}^{2a} = \zeta_m^a$ が成り立つため 69 | 70 | $$ 71 | \begin{aligned} 72 | X_k &= x_0 + \zeta_m^{-k} x_2 + \zeta_m^{-2k} x_4 + \cdots + \zeta_m^{-(m - 1)k} x_{n - 2} \\ 73 | &+ \zeta_n^{-k} (x_1 + \zeta_m^{-k} x_3 + \zeta_m^{-2k} x_5 + \cdots + \zeta_m^{-(m - 1)k} x_{n - 1}) 74 | \end{aligned} 75 | $$ 76 | 77 | となります.このとき,上は長さ $m$ の数列 $(x_0, x_2, x_4, \ldots, x_{n - 2})$ の DFT になっていて,下は長さ $m$ の数列 $(x_1, x_3, x_5, \ldots, x_{n - 1})$ の DFT になっています. 78 | 79 | このように, $(x_0, x_2, x_4, \ldots, x_{n - 2})$ と $(x_1, x_3, x_5, \ldots, x_{n - 1})$ の DFT を用いて $(x_0, x_1, x_2, \ldots, x_{n - 1})$ の DFT を表すことを,**時間間引き**( DIT )といいます. 80 | 81 | # 記法の導入 82 | $\bm{x} = (x_0, x_1, x_2, \ldots, x_{n - 1})$ の添字を 2 進法で表したとき,最下位( 1 の位)が $0$ であるようなものだけを取ってきて $\bm{x}_{:0} = (x_0, x_2, x_4, \ldots, x_{n - 2})$ とします.同様に,最下位が $1$ であるようなものだけを取ってきて $\bm{x}_{:1} = (x_1, x_3, x_5, \ldots, x_{n - 1})$ とします. 83 | 84 | 次に, $\bm{x}_{:0}$ の DFT を $(X_{0:0}, X_{1:0}, X_{2:0}, \ldots, X_{m - 1:0})$, $\bm{x}_{:1}$ の DFT を $(X_{0:1}, X_{1:1}, X_{2:1}, \ldots, X_{m - 1:1})$ とします. 85 | 86 | すると,上の DIT は次のように書くことができます. 87 | 88 | $$ 89 | X_k = X_{k:0} + \zeta_n^{-k} X_{k:1} 90 | $$ 91 | 92 | # 高速フーリエ変換( FFT ) 93 | $m$ も偶数と仮定して $m = 2l$ とおき, $\bm{x}_{:0}$,$\bm{x}_{:1}$ に対してさらに DIT をかけることを考えます. 94 | 95 | $\bm{x}$ の添字を 2 進法で表し,最下位が $0$,$1$ であるようなものを取ってきて,それぞれ $\bm{x}_{:0}$,$\bm{x}_{:1}$ としたのでした.同じ操作を $\bm{x}_{:0}$ に対してもう一度やると,今度は「$\bm{x}$ の添字を 2 進法で表したとき,下 2 桁が $00$,$10$ であるようなもの」が出てきます.これをそれぞれ $\bm{x}_{:00}$,$\bm{x}_{:10}$ とします.同様に,同じ操作を $\bm{x}_{:1}$ に対してやったものを $\bm{x}_{:01}$,$\bm{x}_{:11}$ とします.そして,各 $\iota = 00, 01, 10, 11$ について, $\bm{x}_{:\iota}$ の DFT を $(X_{0:\iota}, X_{1:\iota}, X_{2:\iota}, \ldots, X_{l - 1:\iota})$ とします(変数 $\iota$ は整数ではなく $0$ と $1$ の並びにつけられた名前だと思ってください). 96 | 97 | すると, $\bm{x}_{:0}$,$\bm{x}_{:1}$ の DFT に対する DIT はそれぞれ 98 | 99 | $$ 100 | \begin{aligned} 101 | X_{k:0} &= X_{k:00} + \zeta_m^{-k} X_{k:10} \\ 102 | X_{k:1} &= X_{k:01} + \zeta_m^{-k} X_{k:11} 103 | \end{aligned} 104 | $$ 105 | 106 | となります. 107 | 108 | もし $n$ が $2$ のべき乗ならば,これを数列の長さが $1$ になるまで繰り返すことができます. 109 | 110 | $t$ 回目の DIT を行った後,数列の長さは $\frac{n}{2^t}$ となり,次の DIT は 111 | 112 | $$ 113 | \begin{aligned} 114 | X_{k:\iota} 115 | &= X_{k:0\iota} + \zeta_{\frac{n}{2^t}}^{-k} X_{k:1\iota} \\ 116 | &= X_{k:0\iota} + \zeta_{n}^{-k 2^t} X_{k:1\iota} 117 | \end{aligned} 118 | $$ 119 | 120 | となります. 121 | 122 | 数列 $\bm{x}_{:0\ldots00}$,$\bm{x}_{:0\ldots01}$,$\bm{x}_{:0\ldots10}$,$\ldots$,$\bm{x}_{:1\ldots11}$ の長さが $1$ になったとき,これらは $(x_0)$,$(x_1)$,$(x_2)$,$\ldots$,$(x_{n - 1})$ に他なりません.長さ $1$ の数列は DFT しても変わらないので, 123 | 124 | $$ 125 | \begin{aligned} 126 | (X_{0:0\ldots00}) &= (x_0) \\ 127 | (X_{0:0\ldots01}) &= (x_1) \\ 128 | (X_{0:0\ldots10}) &= (x_2) \\ 129 | \vdots \\ 130 | (X_{0:1\ldots11}) &= (x_{n - 1}) 131 | \end{aligned} 132 | $$ 133 | 134 | となります. 135 | 136 | $X_k$ を求めるには $X_{k:0}$,$X_{k:1}$ が分かればよく, $X_{k:0}$,$X_{k:1}$ を求めるには $X_{k:00}$,$X_{k:01}$,$X_{k:10}$,$X_{k:11}$ が分かればよいので, $X_{0:0\ldots00}$,$X_{0:0\ldots01}$,$X_{0:0\ldots10}$,$\ldots$,$X_{0:1\ldots11}$ が分かれば,逆にたどることで $X_k$ が得られます. 137 | 138 | こうして, DIT を繰り返すことによって DFT を行うのが,**高速フーリエ変換**( FFT )です. 139 | 140 | # 実装 141 | 実装の際は, $X$ の添字 $k:\iota$ を「 $k$ の $2$ 進法表記と $\iota$ を並べて書いて 1 つの数とみなしたもの」として扱います.たとえば, $k = 5$, $\iota = 10$ ならば, $k = 101_{(2)}$ と $10$ を並べて書くと $10110_{(2)} = 22$ なので, $X[22]$ に $X_{5:10}$ の値が入ります. 142 | 143 | 以下に, C++, python, Rust での実装を示します. 144 | ```cpp:FFT の C++ による実装 145 | #include 146 | #include 147 | #include 148 | 149 | constexpr double TAU = 6.28318530717958647692528676655900577; 150 | 151 | void fft(std::vector> &x, const unsigned bit){ 152 | std::size_t n = x.size(); 153 | assert(n == 1 << bit); 154 | std::size_t mask1 = n - 1; // k が数列の長さを超えたときに割った余りをとるマスク 155 | std::vector> zeta(n); 156 | for(std::size_t i = 0; i < n; ++i){ 157 | zeta[i] = std::polar(1., -TAU * i / n); // ゼータの -i 乗 158 | } 159 | for(unsigned i = 0; i < bit; ++i){ 160 | std::size_t mask2 = mask1 >> i + 1; // イオタの部分を得るマスク 161 | std::vector> tmp(n); 162 | for(std::size_t j = 0; j < n; ++j){ 163 | std::size_t lower = j & mask2; // イオタの部分 164 | std::size_t upper = j ^ lower; // k の部分 165 | std::size_t shifted = upper << 1 & mask1; 166 | tmp[j] = x[shifted | lower] + zeta[upper] * x[shifted | mask2 + 1 | lower]; 167 | } 168 | x = std::move(tmp); 169 | } 170 | } 171 | ``` 172 | 173 | ```python:FFT の python による実装 174 | import math 175 | import cmath 176 | 177 | def fft(x, bit): 178 | n = len(x) 179 | assert n == 1 << bit 180 | mask1 = n - 1 # k が数列の長さを超えたときに割った余りをとるマスク 181 | zeta = [cmath.rect(1, -math.tau * i / n) for i in range(n)] # ゼータの 0 乗から -(n - 1) 乗 182 | for i in range(bit): 183 | mask2 = mask1 >> i + 1 # イオタの部分を得るマスク 184 | tmp = [] 185 | for j in range(n): 186 | lower = j & mask2 # イオタの部分 187 | upper = j ^ lower # k の部分 188 | shifted = upper << 1 & mask1 189 | tmp.append(x[shifted | lower] + zeta[upper] * x[shifted | mask2 + 1 | lower]) 190 | x = tmp 191 | return x 192 | ``` 193 | 194 | ```rust:FFT の Rust による実装 195 | use num::complex::Complex64; // 複素数 196 | use std::f64::consts::TAU; // 2π 197 | 198 | fn fft(x: &mut Vec, bit: u32) { 199 | let n = x.len(); 200 | assert_eq!(n, 1 << bit); 201 | let mask1 = n - 1; // k が数列の長さを超えたときに割った余りをとるマスク 202 | let zeta: Vec<_> = (0..n) 203 | .map(|i| Complex64::from_polar(1., -TAU * i as f64 / n as f64)) 204 | .collect(); // ゼータの 0 乗から -(n - 1) 乗 205 | for i in 0..bit { 206 | let mask2 = mask1 >> i + 1; // イオタの部分を得るマスク 207 | *x = (0..n) 208 | .map(|j| { 209 | let lower = j & mask2; // イオタの部分 210 | let upper = j ^ lower; // k の部分 211 | let shifted = upper << 1 & mask1; 212 | x[shifted | lower] + zeta[upper] * x[shifted | mask2 + 1 | lower] 213 | }) 214 | .collect(); 215 | } 216 | } 217 | ``` 218 | 219 | # 終わりに 220 | この記事で扱った実装方法は [satanic さんの github.io](https://satanic0258.github.io/snippets/math/FFT.html) に載っていたもので,[るまさんのブログ](https://tomorinao.blogspot.com/2018/10/various-fft.html)でも解説されています.私は勝手に **satanic FFT** などと呼んでいます. 221 | 222 | # 追記 (2021/10/12) 223 | [同様の手法で実装した NTT](https://judge.yosupo.jp/submission/63348) です. -------------------------------------------------------------------------------- /articles/so-what-is-a-pointer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "C/C++の「ポインタ」とは" 3 | emoji: "📝" 4 | type: "tech" # tech: 技術記事 / idea: アイデア 5 | topics: ["c", "cpp"] 6 | published: true 7 | --- 8 | 9 | C/C++ の「ポインタ」は、難しいとか、実は簡単だとか、色々言われます。 10 | 11 | ポインタについては規格に書かれています。この記事は、ポインタをより正確にイメージするため、規格に私の解釈を追加したものです。区別のため、C++23 草案 (N4950) の内容には節番号を付け、私の解釈は _斜体_ で書きます。 12 | 13 | 私の解釈は以下の記事の影響を受けています。 14 | https://www.ralfj.de/blog/2018/07/24/pointers-and-bytes.html 15 | 16 | # ポインタとは、IDとオフセットの組 17 | まず、_ヌル以外のポインタは、以下の ID とオフセットの組_ です。 18 | 19 | - ID:新しいオブジェクトが作られるたびに振られる、ユニークな値。 20 | - オフセット:オブジェクト先頭からのバイト数。 21 | 22 | 詳しく話していきます。 23 | 24 | ## ID の割り振り 25 | 変数を定義するとオブジェクトが作られ (6.7.2)、_各オブジェクトに固有の ID が割り振られます_。たとえば、 26 | ```cpp 27 | int x, arr[10]; 28 | ``` 29 | と書くと整数 `x` と配列 `arr` が作られるので、`x` と `arr` にそれぞれ ID が振られます。ユニークなので、`x` の ID と `arr` の ID は異なります。 30 | 31 | ただし、ID が振られるのは変数として定義した `x` と `arr` だけで、要素 `arr[0]` `arr[1]` … `arr[9]` に固有の ID は振られません。構造体でも同様で、 32 | ```cpp 33 | struct { int x, y; } foo; 34 | ``` 35 | と書けば `foo` に ID が振られますが、`foo.x` と `foo.y` には振られません。 36 | 37 | `arr[5]` や `foo.x` のように要素やメンバーとして含まれるオブジェクトをサブオブジェクトといい、そうでない `arr` や `foo` を完全なオブジェクトといいます (6.7.2)。_ID は、完全なオブジェクトだけに振られます_。 38 | 39 | _`new` を用いてオブジェクトを作っても、同様に ID が割り振られます_。 40 | ```cpp 41 | int *ptr = new int; 42 | ptr = new int; 43 | ``` 44 | `new` を書くたびに新しいオブジェクトが作られ、異なる ID が振られます。ここでも、振られる ID は 1 度の `new` につき 1 つだけで、サブオブジェクトには振られません。 45 | 46 | ## オフセット 47 | 完全なオブジェクトは、正のサイズを持ちます。サイズは `sizeof` 演算子で得られます。 48 | ```cpp 49 | int arr[10]; 50 | std::cout << sizeof(arr) << std::endl; 51 | ``` 52 | 53 | 私の環境では `int` が 4 バイトなので、40 と出力されました(以下 `int` を 4 バイトとして話を進めます)。 54 | 55 | _この 40 バイトのうちどこか 1 箇所(末尾の直後も可)を指すのが、ポインタです。完全なオブジェクトの中で位置の指定に使う値を、オフセットと呼びます_。 56 | 57 | たとえばポインタが「`arr` の ID」と「オフセット 0」の組み合わせなら、`arr` の 0 バイト目、つまり先頭を指します。これは `arr` 自体で表されます。 58 | 59 | 一方ポインタが「`arr` の ID」と「オフセット 20」の組み合わせなら、`arr` の 20 バイト目、つまり(0 始まりで)5 番目の要素を指します。これは、足し算 `arr + 5` で得られます。 60 | 61 | 「末尾の直後も可」というのは、オフセットが 40 でもよいという意味です。`arr` は 40 バイトしかないので、40 バイト目は存在しない 10 番目の要素ですが、特別に `arr` 末尾の直後のみ許されています。もちろん 41 バイト目や -1 バイト目はなく、オフセットは常に 0 以上 40 以下の値です。 62 | 63 | 実際に書くとこうなります。 64 | ```cpp 65 | int arr[10]; 66 | int *p = arr; // 「arr の ID」と「オフセット 0」 67 | int *q = p + 5; // 「arr の ID」と「オフセット 20」 68 | int *r = q + 5; // 「arr の ID」と「オフセット 40」 69 | ``` 70 | 71 | `arr + 11` や `arr - 1` を計算しようとすると違法(未定義動作)なので (7.6.6)、負のオフセットや 40 を超えるオフセットについて考慮する必要はありません。 72 | 73 | 配列以外でも同様です。 74 | ```cpp 75 | int x; // サイズ 4 76 | int *p = &x; // 「x の ID」と「オフセット 0」 77 | int *q = p + 1; // 「x の ID」と「オフセット 4」 78 | ``` 79 | 80 | ```cpp 81 | struct { int x, y; } foo; // サイズ 8(とします) 82 | int *p = &foo.x; // 「foo の ID」と「オフセット 0」 83 | int *q = &foo.y; // 「foo の ID」と「オフセット 4」 84 | ``` 85 | # ヌルポインタ 86 | どのポインタとも異なる、ヌルポインタ `nullptr` という特別なポインタがあります。 87 | 88 | 足し算 `nullptr + 0` ができることから、サイズ 0 の仮想的なオブジェクトの ID とオフセット 0 の組と考えても良いですが、そうすると `nullptr` は完全なオブジェクトの末尾の直後を指すことにもなり、以下で都合が悪いので、_ID とオフセットの組という形ではない特別なポインタ_ ということにしておきます。 89 | 90 | # `==` `!=` による比較 91 | ポインタ同士は `==` `!=` で比較できます。ここに未定義動作はありません (7.6.10)。 92 | 93 | _原則として、2 つのポインタが等しいというのは、ID とオフセットがともに等しいという意味です_。 94 | 95 | _ただし例外があって、以下を全て満たす場合は結果が未規定です_。 96 | - _ID が異なる_。 97 | - _片方のオフセットが 0_。 98 | - _もう片方のオフセットがサイズと同じ(つまり末尾の直後)_。 99 | 100 | なぜこのような例外があるのでしょうか?それは最適化のためです。 101 | 102 | ID とオフセットを別々に扱うと、各操作につき大変コストがかかるので、コンパイラは ID とオフセットをまとめて 1 つの整数値で表すように翻訳します。この過程で、ある完全オブジェクトの末尾の直後を指すポインタと、別の完全オブジェクトの先頭を指すポインタが、同じ整数値に対応してしまうことがあります。そこで規格は、プログラマーに対し「末尾の直後と先頭を比較しても結果に期待するな」と言うことで、コンパイラがより効率的なプログラムを生成することを許したのです。 103 | 104 | サイズが 0 のオブジェクトが基本的に作れないのも、このためでしょう。 105 | 106 | また、`nullptr == nullptr` は常に真で、`nullptr == (nullptr以外)` は常に偽です。コンパイラは、好きな整数値を `nullptr` に割り当てておき、どのオブジェクトとも被らないようにすれば OK です。 107 | 108 | :::message 109 | 追記:特殊な環境(LISP マシン)ではポインタを 1 つの整数値に最適化できず、2 つ組のまま扱います。上の「未規定」は「常に偽」でもよいので、ヌルポインタとして (NIL, 0) を用いるようです。 110 | ::: 111 | 112 | # `<` `>` `<=` `>=` による比較 113 | ポインタ同士の大小は `<` `>` `<=` `>=` で比較できますが、_基本的には同じ ID のときにオフセットの大小を比較するものです_。 114 | 115 | 同じ配列中の 2 つの要素を指すポインタを比較すれば、ID が同じなので、添字が小さい方が小さく、添字が大きい方が大きくなります。また同じ構造体の 2 つのメンバーを指すポインタを比較すれば、ID が同じなので、前の方が小さく、後の方が大きくなります(ここでいう前後は、構造体を定義するときに宣言した順番です)。 116 | 117 | 一方、ID が異なる 2 つのポインタの大小比較については、矛盾が生じない(たとえば `p < q` と `q < r` と `r < p` は同時に成り立たない)ことが保証されていますが、その具体的な結果は未規定です。`nullptr` とそれ以外の大小比較も未規定です。 118 | 119 | # オブジェクトの生存期間 120 | 上で挙げたような変数宣言や `new` でオブジェクトが作られると、オブジェクトの生存期間(ライフタイム)が始まります (6.7.3)。 121 | 122 | 変数のスコープを抜けたり `delete` を呼んだりしてオブジェクトが破棄されると、生存期間は終了します。 123 | 124 | 上で話した、`==` `!=` `<` `>` `<=` `>=` によるポインタ同士の演算は、たぶん生存期間中のみ有効……だと思いますが、自信ありません。以下の記述がそうだと思います。 125 | > 6.7.3 Lifetime [basic.life] 126 | > 4. The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime. 127 | 128 | # 整数とのキャスト 129 | ポインタと整数の間でキャストができますが、`0` が `nullptr` になることを除いて処理系定義です (7.6.1.10)。 130 | 131 | しかし、`size_t` が十分に大きいとして、 `int x;` に対して `(int *)(size_t)&x` が元の値 `&x` に戻ってくることは保証されています。~~おかげで、[XOR linked list](https://en.wikipedia.org/wiki/XOR_linked_list) が実装できます。~~ 132 | 133 | :::message 134 | 追記:[Azaikaさんのコメント](https://zenn.dev/link/comments/19815fd1b0a680)で教えて頂いた通り、C++20 まで XOR linked list を合法たらしめていた safely-derived pointer の規定が C++23 で消えたため、未保証のようでした。 135 | https://azaika.hateblo.jp/entry/2020/08/09/183606 136 | ::: 137 | 138 | 先頭で紹介した記事 "Pointers Are Complicated, or: What's in a Byte?" ではポインタと整数のキャストを未解決の問題点と指摘していますが、処理系定義なら別に良いんじゃないかと個人的には思います。 139 | 140 | -------------------------------------------------------------------------------- /articles/tex-vs-satysfi.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "TeX と SATySFi 主要記法比較" 3 | emoji: "🐥" 4 | type: "tech" 5 | topics: ["tex", "satysfi"] 6 | published: true 7 | --- 8 | 9 | # SATySFi ってな〜に 10 | [SATySFi](https://github.com/gfngfn/SATySFi) は以下の点において TeX と共通しています: 11 | - 組版ソフトである. 12 | - ソースコードをコンパイルして PDF などに変換できる. 13 | - 本やレポートが書ける. 14 | - 数式が書ける. 15 | 16 | 一方,以下の点において TeX と異なります: 17 | - 文法や型システムなど,プログラミング言語としての部分.SATySFi は静的型付きの関数型言語. 18 | 19 | この記事では,TeX は知ってるけど SATySFi は知らないよ〜という人のために,色々な場面における TeX と SATySFi の記法を比較します. 20 | # 全体の構成 21 | タイトルと著者の入れ方,プリアンブルなど. 22 | 23 | TeX: 24 | ``` 25 | \documentclass[uplatex]{jsarticle} 26 | % プリアンブル 27 | \title{タイトル} 28 | \author{著者} 29 | \begin{document} 30 | \maketitle 31 | 段落1 32 | 33 | 段落2 34 | \end{document} 35 | ``` 36 | SATySFi: 37 | ``` 38 | @require: stdjareport 39 | % プリアンブル 40 | document(| title = {タイトル}; author = {著者} |)'< 41 | +p{ 段落1 } 42 | +p{ 段落2 } 43 | > 44 | ``` 45 | このあたりはだいぶ違いますね. 46 | 47 | TeX は空行で段落を分けますが,SATySFi は `+p{`〜`}` で段落を表します. 48 | # 数式 49 | > 正の整数$n$と実数$a_1, \ldots, a_n$,$b_1, \ldots, b_n$に対し 50 | > 51 | > $$ 52 | \left(\sum_{k=1}^n a_k^2\right) 53 | \left(\sum_{k=1}^n b_k^2\right) 54 | \geq \left(\sum_{k = 1}^n a_k b_k\right)^2. 55 | $$ 56 | 57 | TeX: 58 | ``` 59 | 正の整数$n$と実数$a_1, \ldots, a_n$,$b_1, \ldots, b_n$に対し 60 | \[ 61 | \left(\sum_{k=1}^n a_k^2\right) 62 | \left(\sum_{k=1}^n b_k^2\right) 63 | \geq \left(\sum_{k = 1}^n a_k b_k\right)^2. 64 | \] 65 | ``` 66 | SATySFi: 67 | ``` 68 | 正の整数${n}と実数${a_1, \ldots, a_n},${b_1, \ldots, b_n}に対し 69 | \eqn(${ 70 | \paren{\sum_{k=1}^n a_k^2} 71 | \paren{\sum_{k=1}^n b_k^2} 72 | \geq \paren{\sum_{k=1}^n a_k b_k}^2. 73 | }); 74 | ``` 75 | 76 | 上付き `^2` 下付き `_1` とか `\sum` あたりはおおよそ同じです. 77 | 78 | 文中の数式は TeX だと `$`〜`$`,SATySFi だと `${`〜`}`. 79 | 80 | 別行立て数式は TeX だと `\[`〜`\]` や `equation` 環境を使いますが,SATySFi だと `\eqn(`〜`);` や `+math(`〜`);` を使います. 81 | 82 | TeX で `(`〜`)` の大きさを調節するには `\left` `\right` などを使いますが,SATySFi だと `\paren{`〜`}` だけで大きさが自動調節される括弧になります(プリアンブルで azmath を require しておくと `\p{`〜`}`). 83 | 84 | > $$ 85 | \begin{align} 86 | \Gamma(z) &= \int_0^\infty t^{z-1} e^{-t} dt \\ 87 | &= \left[-t^{z-1} e^{-t}\right]_0^\infty 88 | + (z-1) \int_0^\infty t^{z-2} e^{-t} dt \\ 89 | &= (z-1) \Gamma(z-1) 90 | \end{align} 91 | $$ 92 | 93 | TeX: 94 | ``` 95 | \begin{align} 96 | \Gamma(z) &= \int_0^\infty t^{z-1} e^{-t} dt \\ 97 | &= \left[-t^{z-1} e^{-t}\right]_0^\infty 98 | + (z-1) \int_0^\infty t^{z-2} e^{-t} dt \\ 99 | &= (z-1) \Gamma(z-1) 100 | \end{align} 101 | ``` 102 | SATySFi(azmath なし): 103 | ``` 104 | \align[ 105 | ${| \app{\Gamma}{z} |= \int_0^\infty t^{z-1} e^{-t} \ordd t |}; 106 | ${||= \sqbracket{-t^{z-1} e^{-t}}_0^\infty 107 | + \paren{z-1} \int_0^\infty t^{z-2} e^{-t} \ordd t |}; 108 | ${||= \paren{z-1} \app{\Gamma}{z-1} |} 109 | ]; 110 | ``` 111 | SATySFi(azmath あり): 112 | ``` 113 | \align(${ 114 | | \app{\Gamma}{z} |= \int_0^\infty t^{z-1} e^{-t} \ordd t 115 | ||= \sqbracket{-t^{z-1} e^{-t}}_0^\infty 116 | + \p{z-1} \int_0^\infty t^{z-2} e^{-t} \ordd t 117 | ||= \p{z-1} \app{\Gamma}{z-1} 118 | |}); 119 | ``` 120 | 121 | 複数行にわたる数式は,TeX では `align` 環境を使って `&` と `\\` で位置を揃えますが,SATySFi だと `\align(`〜`);` を使って `|` で区切ります. 122 | 123 | # 章立て 124 | TeX(jsarticle): 125 | ``` 126 | \section{夏休みの宿題} 127 | \subsection{概要} 128 | 多くの学校では夏休みに宿題が課される. 129 | \subsection{主な宿題} 130 | 問題集,日記,読書感想文,自由研究などがある. 131 | \subsection{期限までに出来なかった時にはどうするべきか} 132 | なんとか言い訳を考える. 133 | ``` 134 | SATySFi(stdjabook): 135 | ``` 136 | +section{夏休みの宿題}< 137 | +subsection{概要}< 138 | +p{ 139 | 多くの学校では夏休みに宿題が課される. 140 | } 141 | > 142 | +subsection{主な宿題}< 143 | +p{ 144 | 問題集,日記,読書感想文,自由研究などがある. 145 | } 146 | > 147 | +subsection{期限までに出来なかった時にはどうするべきか}< 148 | +p{ 149 | なんとか言い訳を考える. 150 | } 151 | > 152 | > 153 | ``` 154 | 大きく違いますね.TeX は章や節の先頭に `\section{}` や `subsection{}` を入れるだけですが,SATySFi は `<`〜`>` を使って章や節の始まりと終わりを明記します. 155 | 156 | # 箇条書き 157 | TeX: 158 | ``` 159 | \begin{itemize} 160 | \item ごはん 161 | \item 麺 162 | \begin{itemize} 163 | \item うどん 164 | \item そば 165 | \item ラーメン 166 | \end{itemize} 167 | \item パン 168 | \end{itemize} 169 | ``` 170 | SATySFi: 171 | ``` 172 | \listing{ 173 | * ごはん 174 | * 麺 175 | ** うどん 176 | ** そば 177 | ** ラーメン 178 | * パン 179 | } 180 | ``` 181 | TeX では `itemize` 環境を使って各項目の先頭に `\item` を付けます.一方 SATySFi では `\listing{`〜`}` を使い,各項目の先頭に `*` を付けます.入れ子にするときは,TeX だと新しく `itemize` 環境を始め,SATySFi だと `**` の数を増やします. 182 | 183 | # コマンドの定義 184 | 毎回 `\mathbb{R}` と書く代わりに `\bR` を定義しておく.文字を赤くする `\red` を定義しておく.どちらもよくあるコマンド定義です. 185 | 186 | TeX: 187 | ``` 188 | \documentclass[uplatex]{jsarticle} 189 | 190 | \usepackage{amssymb} 191 | \newcommand{\bR}{\mathbb{R}} 192 | \usepackage{color} 193 | \newcommand{\red}[1]{\textcolor{red}{#1}} 194 | 195 | \begin{document} 196 | $\bR$を\red{実数}全体の集合とする. 197 | \end{document} 198 | ``` 199 | SATySFi: 200 | ``` 201 | @require: stdjareport 202 | 203 | let-math \bR = ${\mathbb{R}} 204 | let-inline ctx \red it = 205 | let red = RGB(1., 0., 0.) in 206 | read-inline (set-text-color red ctx) it 207 | in 208 | 209 | document(| title = {}; author = {} |)'< 210 | +p{ 211 | ${\bR}を\red{実数}全体の集合とする. 212 | } 213 | > 214 | ``` 215 | これもけっこう雰囲気が違いますね. 216 | 217 | SATySFi は数式コマンドを `let-math` で,インラインコマンド(文中で使うコマンド)を `let-inline` で定義します.TeX はどちらも `\newcommand` です. 218 | 219 | SATySFi は OCaml や F# の影響を大きく受けているだけあって,関数型言語の記法ですね. 220 | 221 | ちなみに文中の `${\bR}` は(デフォルトの場合)`\math(${\bR});` と同じ意味になっていて,後者を使って書くと以下のようにローカルな(その場だけで有効な)コマンドを定義することもできます. 222 | ``` 223 | @require: stdjareport 224 | 225 | document(| title = {}; author = {} |)'< 226 | +p{ 227 | \math( 228 | let-math \bR = ${\mathbb{R}} in 229 | ${\bR} 230 | );を実数全体の集合とする. 231 | 232 | % ここで ${\bR} は使えない 233 | } 234 | > 235 | ``` 236 | # 表 237 | > | | TeX | SATySFi | 238 | > |--|:--:|:--:| 239 | > | 作者 | D. Knuth | gfngfn | 240 | > | 開発年 | 1978 | 2017 | 241 | TeX: 242 | ``` 243 | \documentclass[uplatex]{jsarticle} 244 | \begin{document} 245 | \begin{tabular}{lcc} 246 | \hline 247 | & \TeX & SATySFi \\ 248 | \hline 249 | 作者 & D. Knuth & gfngfn \\ 250 | 開発年 & 1978 & 2017 \\ 251 | \hline 252 | \end{tabular} 253 | \end{document} 254 | ``` 255 | SATySFi: 256 | ``` 257 | @require: stdjareport 258 | @require: easytable/easytable 259 | open EasyTableAlias 260 | in 261 | 262 | document(| title = {}; author = {} |)'< 263 | +p{ 264 | \easytable[l;c;c]{ 265 | | | \TeX; | \SATySFi; 266 | | 作者 | D. Knuth | gfngfn 267 | | 開発年 | 1978 | 2017 268 | |} 269 | } 270 | > 271 | ``` 272 | こちらも,TeX は `&` と `\\` で区切るのに対し,SATySFi は `|` で区切ります. 273 | 274 | 他に何か思いついたら書き足します. -------------------------------------------------------------------------------- /books/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/.keep -------------------------------------------------------------------------------- /books/apg4b-rust-ver/config.yaml: -------------------------------------------------------------------------------- 1 | title: "AtCoder と Rust で始める!競技プログラミング入門(Rust 版 APG4b)" 2 | summary: "この本の内容は, RustCoder ( https://zenn.dev/toga/books/rust-atcoder )に移りました." 3 | topics: [] 4 | published: true 5 | price: 0 6 | chapters: 7 | - message 8 | -------------------------------------------------------------------------------- /books/apg4b-rust-ver/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/cover.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/arithmetic_right_shift_negative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/arithmetic_right_shift_negative.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/arithmetic_right_shift_positive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/arithmetic_right_shift_positive.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/array_reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/array_reference.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/bitwise_and.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/bitwise_and.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/bitwise_not.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/bitwise_not.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/bitwise_or.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/bitwise_or.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/bitwise_xor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/bitwise_xor.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/format.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/format.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 27 | 32 | 33 | 41 | 46 | 47 | 55 | 60 | 61 | 69 | 74 | 75 | 83 | 88 | 89 | 97 | 102 | 103 | 104 | 123 | 125 | 126 | 128 | image/svg+xml 129 | 131 | 132 | 133 | 134 | 135 | 139 | println!("{2} {3} {0} {4} {1}", -2, 10, 2.4, 30, 4.5); 152 | (0) 165 | (1) 178 | (2) 191 | (3) 204 | (4) 217 | 224 | 231 | 238 | 245 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/left_shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/left_shift.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/logical_right_shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/logical_right_shift.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_array.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_complex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_complex.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_ref_mut_tuple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_ref_mut_tuple.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_ref_single.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_ref_single.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_ref_tuple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_ref_tuple.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_reference.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_struct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_struct.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_tuple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_tuple.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/pattern_tuple_struct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/pattern_tuple_struct.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/quick_sort.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 27 | 32 | 33 | 34 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 63 | 64 | 65 | 69 | 80 | 3 6 5 1 2 6 8 8 8 9 93 | 6 6 8 8 2 3 8 1 5 9 106 | 112 | 118 | 124 | pivot 137 | 全て pivot 以下 150 | 全て pivot より大きい 163 | 169 | pivot 182 | 188 | 194 | 200 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/quick_sort_split.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/quick_sort_split.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/reference1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/reference1.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/reference2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/reference2.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/images/reference3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/apg4b-rust-ver/images/reference3.png -------------------------------------------------------------------------------- /books/apg4b-rust-ver/message.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "移動について" 3 | --- 4 | この本の内容は『 [RustCoder ―― AtCoder と Rust で始める競技プログラミング入門](https://zenn.dev/toga/books/rust-atcoder)』へと移動しました.そちらをお読みください. 5 | -------------------------------------------------------------------------------- /books/llvm-toy-language/01-intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "序" 3 | --- 4 | 5 | # LLVM でなんかしたい 6 | 7 | 遊びで言語処理系を作ってみようって感じ. 8 | 9 | この本は,自分用のメモ的な感覚で書いてるので,あんまりしっかりはしてないかも. 10 | 11 | 書いたコードは[ここ](https://github.com/fiveseven-lambda/toy_language)に置いてる. 12 | 13 | 気長〜に続けていきたい…… 14 | 15 | # C++ で文字列を扱うのは,めんどくさい. 16 | 目的はあくまで,LLVM を使って何かしてみること.文字列処理に苦しみたいわけじゃない. 17 | 18 | そこで,今回作る言語のソースコードは,[lex.charset] にある basic source character set のみで書かれているものとする. 19 | 20 | コメントも例外ではない(日本語でコメント書けないのはつらいけど).~~でも,実装的には UTF-8 なら日本語でコメント書いても大丈夫だと思う~~ 21 | 22 | ASCII にあるドル `$` とかバッククォート `` ` `` が無いけれど,それなりの言語は作れるだろう(C++ 自体がそうなわけだし). 23 | 24 | # ソースコード末尾の改行 25 | ソースコードの末尾には,改行が必ず付いているものとする.そうするとちょっと楽になる. 26 | 27 | # JIT コンパイラ 28 | 1 文ずつコンパイルして実行する. 29 | 30 | **モジュール**というのは LLVM の翻訳単位.よって 1 つの文が 1 つのモジュールとなる. -------------------------------------------------------------------------------- /books/llvm-toy-language/02-error.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "エラー報告" 3 | --- 4 | # Pos 5 | エラーが起こったとき,どこでなぜエラーが起こったのか教えてもらえないととても嫌な気持ちになる. 6 | 7 | じゃあその「場所」はどうやって表そうか,となる.大抵の言語は,何行目の何バイト目,のように教えてくれると思う.じゃあそれを表現するクラスを作ろう. 8 | ```cpp:pos.hpp 9 | #include 10 | #include 11 | #include 12 | 13 | namespace pos { 14 | class Pos { 15 | std::size_t line, byte; 16 | public: 17 | Pos(); 18 | Pos(std::size_t, std::size_t); 19 | std::pair into_inner() const; 20 | friend std::ostream &operator<<(std::ostream &, const Pos &); 21 | }; 22 | } 23 | ``` 24 | ```cpp:pos.cpp 25 | #include "pos.hpp" 26 | 27 | namespace pos { 28 | Pos::Pos() = default; 29 | Pos::Pos(std::size_t line, std::size_t byte): line(line), byte(byte) {} 30 | 31 | std::pair Pos::into_inner() const { 32 | return {line, byte}; 33 | } 34 | 35 | std::ostream &operator<<(std::ostream &os, const Pos &pos){ 36 | return os << pos.line + 1 << ":" << pos.byte + 1; 37 | } 38 | } 39 | ``` 40 | あっ,インクルードガードはここでは省略する.実際のコードではちゃんと書いてる. 41 | 42 | `line`,`byte` は 0-indexed で持つことにする.ソースコードの先頭は 0 行目 0 バイト目だ.でも,エラーメッセージまで 0-indexed だと嫌そう(エディタの行数表示は普通 1-indexed だよね)なので,出力のときに 1-indexed に直す. 43 | 44 | コンストラクタはなんとなく要りそうなものを書いている.もしかしたら後で変えるかもしれない. 45 | 46 | `into_inner` は中身を `std::pair` にして返す.何かと使える. 47 | # Range 48 | `Pos` を使うとある 1 文字の位置を表現できる.一方で,式みたいな複数文字にわたるものの場所は「ここからここまで」みたく範囲で表される. 49 | 50 | じゃあそれは `Pos` 2 つの組で表そう. 51 | ```cpp:pos.hpp 52 | namespace pos { 53 | class Range { 54 | Pos start, end; 55 | public: 56 | Range(); 57 | Range(std::size_t, std::size_t, std::size_t); 58 | friend std::ostream &operator<<(std::ostream &, const Range &); 59 | }; 60 | } 61 | ``` 62 | ```cpp:pos.cpp 63 | namespace pos { 64 | Range::Range() = default; 65 | Range::Range(std::size_t line, std::size_t start, std::size_t end): 66 | start(line, start), 67 | end(line, end) {} 68 | 69 | std::ostream &operator<<(std::ostream &os, const Range &range){ 70 | auto [sline, sbyte] = range.start.into_inner(); 71 | auto [eline, ebyte] = range.end.into_inner(); 72 | return os 73 | << sline + 1 << ":" << sbyte + 1 74 | << "-" << eline + 1 << ":" << ebyte; 75 | } 76 | } 77 | ``` 78 | 区間を持つとき,開区間,閉区間,半開区間のどれが一番いいだろうか.これは後で lexer を書くときに分かって,半開区間がいい.`start` は含み,`end` は含まない.これも,エラーメッセージまで半開区間だとたぶん嫌なので,出力のときに閉区間に直す. 79 | 80 | # eprint 81 | 正直,コンパイラに何行目何文字目って数字だけ言われてもパッと分からないこともあると思う(行数の表示されないエディタを使っていたり?).エラーメッセージ自体にソースコードの一部があった方がなんかいい. 82 | 83 | `Pos` や `Range` は,何行目何文字目っていう数字を持っている.じゃあ,ソースコードを読みながら全体をどこかに保存しておいて,`Pos` や `Range` に渡すことでその中から当該の部分文字列を切り取ってくれるようにしたらいいんじゃないか?入力は対話っぽく stdin で与えられるかもしれないので,あとで見たいなら変数に保存しておくしかない. 84 | 85 | 今回はソースコードを 1 行ずつに分けて `std::vector` に入れておこうと思う.以下を追記だ. 86 | 87 | ```cpp:pos.hpp 88 | #include 89 | #include 90 | 91 | namespace pos { 92 | class Pos { 93 | /*略 */ 94 | void eprint(const std::vector &) const; 95 | }; 96 | 97 | class Range { 98 | /*略 */ 99 | void eprint(const std::vector &) const; 100 | }; 101 | } 102 | ``` 103 | 中身は省略.好みで出力する. 104 | 105 | # Error 106 | エラーはいろんなエラーがある.持ちたいメンバも,エラーの種類ごとに違いそうだ. 107 | 108 | そういうときは抽象クラスにする. 109 | ```cpp:error.hpp 110 | #include 111 | #include 112 | #include 113 | 114 | namespace error { 115 | class Error { 116 | public: 117 | virtual ~Error(); 118 | void virtual eprint(const std::vector &) const = 0; 119 | }; 120 | 121 | template 122 | std::unique_ptr make(Args&&... args){ 123 | return std::make_unique(std::forward(args)...); 124 | } 125 | } 126 | ``` 127 | ```cpp:error.cpp 128 | #include "error.hpp" 129 | 130 | namespace error { 131 | Error::~Error() = default; 132 | } 133 | ``` 134 | `eprint()` がエラーメッセージを stderr に出力する関数となる.メンバとして `Pos` や `Range` を持っているエラーであれば,`Pos::eprint()` や `Range::eprint()` に引数の `std::vector` を渡して場所を出力させるだろう. 135 | 136 | `make()` はヘルパ関数だ.これを使えば,`static_cast(std::make_unique(bar))` が単に `error::make(bar)` と書けて楽になる. -------------------------------------------------------------------------------- /books/llvm-toy-language/03-token.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "トークン" 3 | --- 4 | 5 | 字句解析のために,まずはトークンを定義する. 6 | 7 | # Token 8 | まずは基底クラスから作る. 9 | 10 | ```cpp:token.hpp 11 | #include 12 | #include 13 | 14 | #include "pos.hpp" 15 | 16 | namespace token { 17 | class Token { 18 | public: 19 | pos::Range pos; 20 | virtual ~Token(); 21 | virtual void debug_print() const = 0; 22 | }; 23 | } 24 | ``` 25 | ```cpp:token.cpp 26 | #include "token.hpp" 27 | 28 | namespace token { 29 | Token::~Token() = default; 30 | } 31 | ``` 32 | `debug_print()` はデバッグ出力用の関数だ.いずれ消す. 33 | 34 | トークンは複数文字にわたるので,位置を表すのに `pos::Range` を使う. 35 | # Identifier 36 | 識別子は,正規表現 `[A-Za-z_][A-Za-z_0-9]*` に合致するものとする. 37 | 38 | ```cpp:token.hpp 39 | #include 40 | 41 | namespace token { 42 | class Identifier : public Token { 43 | std::string name; 44 | public: 45 | Identifier(std::string); 46 | void debug_print() const override; 47 | }; 48 | } 49 | ``` 50 | ```cpp:token.cpp 51 | #include 52 | 53 | namespace token { 54 | Identifier::Identifier(std::string name): name(std::move(name)) {} 55 | void Identifier::debug_print() const { 56 | std::cout << "Identifier(" << name << ")" << std::endl; 57 | } 58 | } 59 | ``` 60 | # Integer 61 | 整数リテラルは `[0-9]+`. 62 | 63 | ゆくゆくは `0b` `0o` `0x` で始まる 2,8,16 進リテラルや浮動小数点数リテラルも考えたいけど,今はやめておく. 64 | 65 | 整数なのにメンバ `value` の型は `std::string`.あとでこれを 32 ビット整数値に直すとき `2147483648` 以上はエラーとするけれど,前に `-` が付いた `-2147483648` は ok とする. 66 | ```cpp:token.hpp 67 | namespace token { 68 | class Integer : public Token { 69 | std::string value; 70 | public: 71 | Integer(std::string); 72 | void debug_print() const override; 73 | }; 74 | } 75 | ``` 76 | ```cpp:token.cpp 77 | namespace token { 78 | Integer::Integer(std::string value): value(std::move(value)) {} 79 | void Integer::debug_print() const { 80 | std::cout << "Integer(" << value << ")" << std::endl; 81 | } 82 | } 83 | ``` 84 | # 記号類 85 | 何があるといい? 86 | - 四則演算 `+` `-` `*` `/` `%` 87 | - ビット演算 `~` `<<` `>>` `&` `|` `^` 88 | - 比較 `==` `!=` `<` `>` `<=` `>=` 89 | - 論理演算 `&&` `||` `!` 90 | - 代入 `=` `+=` `-=` `*=` `/=` `%=` `<<=` `>>=` `&=` `|=` `^=` 91 | - 括弧 `(` `)` `{` `}` `[` `]` 92 | - 区切り文字 `.` `:` `;` `,` 93 | ```cpp:token.hpp 94 | namespace token { 95 | class Plus : public Token { 96 | void debug_print() const override; 97 | }; 98 | class Hyphen : public Token { 99 | void debug_print() const override; 100 | }; 101 | /* 以下略 */ 102 | } 103 | ``` 104 | ```cpp:token.cpp 105 | #define define_debug_print(token) \ 106 | void token::debug_print() const { \ 107 | std::cout << #token << std::endl; \ 108 | } 109 | 110 | namespace token { 111 | define_debug_print(Plus) 112 | define_debug_print(Hyphen) 113 | /* 略 */ 114 | } 115 | ``` 116 | いずれここにたくさん純粋仮想関数を追加する. 117 | 118 | 119 | 使える文字の中ではあと `#` と `?` が残ってる. -------------------------------------------------------------------------------- /books/llvm-toy-language/04-lexer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "字句解析" 3 | --- 4 | 5 | いよいよ字句解析を始めよう. 6 | 7 | 対話環境を実現したいので,入出力と字句解析は完全に切り離せない.これがちょっと面倒を生む. 8 | 9 | # Inner(ヘルパ) 10 | 字句解析は,`Lexer` 構造体が担う. 11 | ```cpp:lexer.hpp 12 | class Lexer; 13 | ``` 14 | ……わけなのだが,`Lexer` は入出力とか peek だけを扱い,実際に文字列からトークンへの分解を行う部分は `Inner` 構造体に任せる. 15 | 16 | ```cpp:lexer.hpp 17 | #include 18 | #include 19 | #include 20 | 21 | #include "token.hpp" 22 | 23 | class Inner { 24 | public: 25 | void run( 26 | std::size_t, 27 | const std::string &, 28 | std::queue> & 29 | ); 30 | }; 31 | ``` 32 | ```cpp:lexer.cpp 33 | #include "lexer.hpp" 34 | 35 | void Inner::run( 36 | std::size_t line_num, 37 | const std::string &str, 38 | std::queue> &queue 39 | ){ 40 | /* queue にトークンを追加 */ 41 | } 42 | ``` 43 | 44 | `Inner::run()` は,引数として 45 | - `line_num`(`std::size_t`):今見ている行番号 46 | - `str`(`const std::string &`):今見ている行 47 | - `queue`(`std::queue> &`):分解したトークンの保存先 48 | 49 | を受け取る.`Lexer` が入力を 1 行読み,`Inner` に「この queue に追加しておいてね〜」というと,`Inner` はその行をトークンに分解して queue に格納するわけだ. 50 | 51 | `Inner::run()` の具体的な実装は後回しにして,その前に `Lexer` の方を完成させる. 52 | # Lexer 53 | `Lexer` の仕事は,入力を読んで `Inner` に分解させ,トークンを返すイテレータとなることだ. 54 | ```cpp:lexer.hpp 55 | #include 56 | #include 57 | #include 58 | 59 | class Lexer { 60 | std::istream &source; 61 | bool prompt; 62 | Inner inner; 63 | std::vector log; 64 | std::queue> tokens; 65 | public: 66 | Lexer(); 67 | Lexer(std::ifstream &); 68 | const std::vector &get_log() const; 69 | std::unique_ptr next(), &peek(); 70 | }; 71 | ``` 72 | 対話環境では `> ` みたいなプロンプトが出るとうれしい.それを出すかどうかはメンバ変数 `prompt` で切り替える. 73 | 74 | コンストラクタは,対話環境用とファイルからの入力用の 2 つを用意する. 75 | 76 | ```cpp:lexer.cpp 77 | Lexer::Lexer(): 78 | source(std::cin), 79 | prompt(true) {} 80 | Lexer::Lexer(std::ifstream &source): 81 | source(source), 82 | prompt(false) {} 83 | ``` 84 | 85 | 受け取った入力は `log` に保存しておいて,`get_log()` で手に入るようにする. 86 | 87 | ```cpp:lexer.cpp 88 | const std::vector &Lexer::get_log() const { 89 | return log; 90 | } 91 | ``` 92 | `next()`,`peek()` は `Lexer` がイテレータとして働くための関数.EOF に達したら,`nullptr` を返す. 93 | ```cpp:lexer.cpp 94 | std::unique_ptr &Lexer::peek(){ 95 | while(tokens.empty()){ 96 | if(source){ 97 | // 次に読むのは何行目か (0-indexed) 98 | auto line_num = log.size(); 99 | // 空の行を追加 100 | log.emplace_back(); 101 | // 対話環境ならプロンプトを出す 102 | if(prompt) std::cout << "> "; 103 | // 1 行読む 104 | std::getline(source, log.back()); 105 | // トークンに分解 106 | inner.run(line_num, log.back(), tokens); 107 | }else{ 108 | // EOF に達したので nullptr を追加 109 | tokens.emplace(); 110 | } 111 | } 112 | return tokens.front(); 113 | } 114 | std::unique_ptr Lexer::next(){ 115 | auto ret = std::move(peek()); 116 | tokens.pop(); 117 | return ret; 118 | } 119 | ``` 120 | # `Inner::run()` 121 | さて,`Inner::run()` の中身を埋めていく. 122 | ```cpp:lexer.cpp 123 | void Inner::run( 124 | std::size_t line_num, 125 | const std::string &str, 126 | std::queue> &queue 127 | ){ 128 | /* 中身 */ 129 | } 130 | ``` 131 | まずは,文字列を走査するためのカーソル `cursor` を作る.「何バイト目か」という数字が `pos::Range` を作るときに必要なので,`cursor` の型は `std::size_t` にしておいて,各文字を見るには `str[cursor]` と書こう.これをイテレータにしておいて,`std::distance` を使ってもいいと思う. 132 | ```cpp:lexer.cpp 133 | std::size_t cursor = 0; 134 | ``` 135 | 136 | 次に `while` 文を回す.1 回のループでは,1 個のトークンを読み,読んだ文字数だけ `cursor` を進める. 137 | 138 | ループ内では,まず空白を読み飛ばす.この間に行末に達したら,その行にトークンはもう残っていないので関数を抜ける. 139 | ```cpp:lexer.cpp 140 | while(true){ 141 | // この中でトークンを 1 個読む 142 | 143 | // 空白を読み飛ばす 144 | while(true){ 145 | if(cursor == str.size()) return; 146 | if(!std::isspace(str[cursor])) break; 147 | ++cursor; 148 | } 149 | std::size_t start = cursor; 150 | /* str[start] はトークンの先頭の文字 */ 151 | 152 | } 153 | ``` 154 | あとは,1 文字目が数字なら整数リテラル,アルファベットなら識別子……という具合にトークンを読んで,queue に格納する. 155 | 156 | ここで,こんなのを用意しておくと都合がいい. 157 | ```cpp:lexer.cpp 158 | auto advance_if = [&](char c){ 159 | bool ret = str[cursor] == c; 160 | if(ret) ++cursor; 161 | return ret; 162 | }; 163 | ``` 164 | 165 | 使ってる様子を見たら便利なのが分かると思う. 166 | ```cpp:lexer.cpp 167 | std::unique_ptr token; 168 | if(std::isdigit(str[start])){ 169 | // 整数リテラル 170 | while(std::isdigit(str[cursor])) ++cursor; 171 | token = std::make_unique(str.substr(start, cursor - start)); 172 | }else if(std::isalpha(str[start]) || str[start] == '_'){ 173 | // 識別子 174 | while(std::isalnum(str[cursor]) || str[cursor] == '_') ++cursor; 175 | token = std::make_unique(str.substr(start, cursor - start)); 176 | }else if(advance_if('+')){ 177 | if(advance_if('=')) token = std::make_unique(); 178 | else token = std::make_unique(); 179 | }else if(advance_if('<')){ 180 | if(advance_if('<')) 181 | if(advance_if('=')) token = std::make_unique(); 182 | else token = std::make_unique(); 183 | else if(advance_if('=')) token = std::make_unique(); 184 | else token = std::make_unique(); 185 | }else if /* 中略.他の記号についても同様 */ 186 | }else{ 187 | // 知らない文字が現れたらエラー 188 | throw error::make(pos::Pos(line_num, cursor)); 189 | } 190 | // pos を設定 191 | token->pos = pos::Range(line_num, start, cursor); 192 | // queue に追加 193 | queue.push(std::move(token)); 194 | ``` 195 | 196 | 最後の `error::UnexpectedCharacter` は次のように定義しておく. 197 | ```cpp:error.hpp 198 | #include "pos.hpp" 199 | 200 | namespace error { 201 | class UnexpectedCharacter : public Error { 202 | pos::Pos pos; 203 | public: 204 | UnexpectedCharacter(pos::Pos); 205 | void eprint(const std::vector &) const override; 206 | }; 207 | } 208 | ``` 209 | ```cpp:error.cpp 210 | namespace error { 211 | UnexpectedCharacter::UnexpectedCharacter(pos::Pos pos): 212 | pos(std::move(pos)) {} 213 | void UnexpectedCharacter::eprint(const std::vector &log) const { 214 | std::cerr << "unexpected character at " << pos << std::endl; 215 | pos.eprint(log); 216 | } 217 | } 218 | ``` 219 | 220 | # コメント 221 | `//` から行末までのラインコメント,`/*` で始まり `*/` で終わるブロックコメントを実装する. 222 | 223 | ラインコメントは,さっきの `Lexer::inner()` を少し書き換えて,`/` で始まるトークンの部分を 224 | ```cpp:lexer.cpp 225 | }else if(advance_if('/')){ 226 | if(advance_if('=')){ 227 | token = std::make_unique(); 228 | }else if(str[cursor] == '/'){ 229 | // 以降は全てラインコメントなので, 230 | // return で字句解析を終える 231 | return; 232 | }else{ 233 | token = std::make_unique(); 234 | } 235 | }else if(advance_if('%')){ /* ... */ 236 | ``` 237 | とすればいい. 238 | 239 | 一方複数行にわたるブロックコメントの方はどうすればいいか.ある行でブロックコメントが始まって,次の行まで続く場合,`Inner::run()` は前の行からコメントが続いていることを知らなければならない.よって,コメント中という情報を `Inner` にメンバとしてもたせる必要がある. 240 | 241 | また,ブロックコメントの途中で EOF に達してしまった場合は,エラーとしたい.エラーメッセージには,コメントがどこから始まったのかも書くと親切だろう.よって,コメントの開始場所も覚えておく. 242 | 243 | ブロックコメントはネスト可能にもしたい. 244 | 245 | そうすると,`Inner` には `std::vector` をもたせておこうとなる. 246 | ```cpp:lexer.hpp 247 | #include 248 | 249 | class Inner { 250 | std::vector comment; 251 | /* 略 */ 252 | }; 253 | ``` 254 | 255 | コメントが始まったら,その位置を `comment` に push back する.コメントを抜けたら pop back する.`comment` が空でなければコメントの途中だと分かる. 256 | 257 | push back はどこに書くかというと,さっきのラインコメントの開始と同じ場所. 258 | ```cpp:lexer.cpp 259 | }else if(advance_if('/')){ 260 | if(advance_if('=')){ 261 | token = std::make_unique(); 262 | }else if(str[cursor] == '/'){ 263 | return; 264 | }else if(advance_if('*')){ 265 | // ブロックコメントの開始 266 | comment.emplace_back(line_num, start); 267 | continue; 268 | }else{ 269 | token = std::make_unique(); 270 | } 271 | }else if(advance_if('%')){ 272 | ``` 273 | `continue;` しないと,無のトークンをキューに追加する操作が走ってしまう. 274 | 275 | また,ここで `advance_if('*')` の代わりに `str[cursor] == '*'` としてしまうと,`/*/` が正しく読めない(コメントが即座に終わってしまう). 276 | 277 | `*/` が来るまでコメントとして読み飛ばす処理はどこに書くかというと,空白を読み飛ばすのに使っていた `while` 文. 278 | ```cpp:lexer.cpp 279 | // 空白を読み飛ばす 280 | // コメントも読み飛ばす 281 | while(true){ 282 | if(cursor == str.size()) return; 283 | if(!comment.empty()){ 284 | // コメント中. 285 | // もし残り 2 文字以上あれば, 286 | if(cursor < str.size() - 1){ 287 | // コメントが開始したり終了したりしないか調べる 288 | if(str[cursor] == '*' && str[cursor + 1] == '/'){ 289 | // コメントの終了 290 | comment.pop_back(); 291 | // 2 文字( `*/` の分)読み進める 292 | cursor += 2; 293 | continue; 294 | }else if(str[cursor] == '/' && str[cursor + 1] == '*'){ 295 | // ネストされたコメントの開始. 296 | comment.emplace_back(line_num, cursor); 297 | // 2 文字( `/*` の分)読み進める 298 | cursor += 2; 299 | continue; 300 | } 301 | } 302 | }else if(!std::isspace(str[cursor])) break; 303 | ++cursor; 304 | } 305 | ``` 306 | `/*` や `*/` が現れたときには,ちゃんとそれらの文字数ぶんカーソルを進めなければいけない. 307 | 308 | コメントの途中で EOF に達したらエラーだ. 309 | ```cpp:error.hpp 310 | namespace error { 311 | class UnterminatedComment : public Error { 312 | std::vector poss; 313 | public: 314 | UnterminatedComment(std::vector); 315 | void eprint(const std::vector &) const override; 316 | }; 317 | } 318 | ``` 319 | 中身は省略する. 320 | 321 | EOF はどこで判明するかというと,`Lexer::peek()` の中.ここで,EOF のときの処理を `Inner` に頼む. 322 | ```cpp:lexer.cpp 323 | token::TokenWithPos &Lexer::peek(){ 324 | while(tokens.empty()){ 325 | if(source){ 326 | /* 略 */ 327 | }else{ 328 | inner.deal_with_eof(); 329 | tokens.emplace(); 330 | } 331 | } 332 | return tokens.front(); 333 | } 334 | ``` 335 | EOF の処理をする関数 `deal_with_eof()` を `Inner` に追加して, 336 | ```cpp:lexer.hpp 337 | class Inner { 338 | /* 略 */ 339 | public: 340 | void deal_with_eof(); 341 | }; 342 | ``` 343 | その中で `comment` が空かどうかチェックする. 344 | ```cpp:lexer.cpp 345 | void Inner::deal_with_eof(){ 346 | if(!comment.empty()){ 347 | throw error::make(std::move(comment)); 348 | } 349 | } 350 | ``` -------------------------------------------------------------------------------- /books/llvm-toy-language/05-expression.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "構文木 (式)" 3 | --- 4 | 5 | 構文解析は,式の解析から始める. 6 | 7 | 準備としてまず AST を定義する. 8 | 9 | # Expression 10 | まずは基底クラスから作る. 11 | ```cpp:expression.hpp 12 | #include 13 | #include 14 | 15 | #include "pos.hpp" 16 | 17 | namespace expression { 18 | class Expression { 19 | public: 20 | pos::Range pos; 21 | virtual ~Expression(); 22 | virtual void debug_print(int depth = 0) const = 0; 23 | }; 24 | } 25 | ``` 26 | ```cpp:expression.cpp 27 | #include "expression.hpp" 28 | 29 | namespace expression { 30 | Expression::~Expression() = default; 31 | } 32 | ``` 33 | `debug_print()` はデバッグ出力用.式はネストするので,デバッグ出力時は `debug_print()` が再帰的に呼ばれる感じになる.引数 `depth` はその再帰の深さで,出力をインデントするのに使う.イメージとしては,`(1 + 2) * (3 - 4)` が 34 | ``` 35 | Mul 36 | Add 37 | 1 38 | 2 39 | Sub 40 | 3 41 | 4 42 | ``` 43 | とか 44 | ``` 45 | 1 46 | Add 47 | 2 48 | Mul 49 | 3 50 | Sub 51 | 4 52 | ``` 53 | みたくなってほしい. 54 | 55 | # Identifier 56 | 単一の識別子は,式. 57 | ```cpp:expression.hpp 58 | #include 59 | 60 | namespace expression { 61 | class Identifier : public Expression { 62 | std::string name; 63 | public: 64 | Identifier(std::string); 65 | void debug_print(int) const override; 66 | }; 67 | } 68 | ``` 69 | ```cpp:expression.cpp 70 | #include 71 | #include 72 | 73 | namespace expression { 74 | Identifier::Identifier(std::string name): name(std::move(name)) {} 75 | 76 | static constexpr std::string_view INDENT = " "; 77 | void Identifier::debug_print(int depth) const { 78 | for(int i = 0; i < depth; ++i) std::cout << INDENT; 79 | std::cout << pos << ": Identifier(" << name << ")" << std::endl; 80 | } 81 | } 82 | ``` 83 | # Integer 84 | 単一の整数リテラルも,式. 85 | ```cpp:expression.hpp 86 | #include 87 | 88 | namespace expression { 89 | class Integer : public Expression { 90 | std::int32_t value; 91 | public: 92 | Integer(std::int32_t); 93 | void debug_print(int) const override; 94 | }; 95 | } 96 | ``` 97 | ```cpp:expression.cpp 98 | namespace expression { 99 | Integer::Integer(std::int32_t value): value(value) {} 100 | 101 | void Integer::debug_print(int depth) const { 102 | for(int i = 0; i < depth; ++i) std::cout << INDENT; 103 | std::cout << pos << ": Integer(" << value << ")" << std::endl; 104 | } 105 | } 106 | ``` 107 | # Unary 108 | 式の前に単項演算子が付いたものも式. 109 | 110 | まずは演算子を表す `enum class` を定義して, 111 | ```cpp:expression.hpp 112 | namespace expression { 113 | enum class UnaryOperator { 114 | Plus, Minus, 115 | LogicalNot, BitNot 116 | }; 117 | } 118 | ``` 119 | 演算子と式の組として定義する. 120 | ```cpp:expression.hpp 121 | namespace expression { 122 | class Unary : public Expression { 123 | UnaryOperator unary_operator; 124 | std::unique_ptr operand; 125 | public: 126 | Unary(UnaryOperator, std::unique_ptr); 127 | void debug_print(int) const override; 128 | }; 129 | } 130 | ``` 131 | ```cpp:expression.cpp 132 | namespace expression { 133 | Unary::Unary( 134 | UnaryOperator unary_operator, 135 | std::unique_ptr operand 136 | ): 137 | unary_operator(unary_operator), 138 | operand(std::move(operand)) {} 139 | 140 | void Unary::debug_print(int depth) const { 141 | std::string_view name; 142 | switch(unary_operator){ 143 | case UnaryOperator::Plus: name = "plus"; break; 144 | case UnaryOperator::Minus: name = "minus"; break; 145 | case UnaryOperator::LogicalNot: name = "logical not"; break; 146 | case UnaryOperator::BitNot: name = "bitwise not"; 147 | } 148 | for(int i = 0; i < depth; ++i) std::cout << INDENT; 149 | std::cout << pos << ": Unary(" << name << ")" << std::endl; 150 | operand->debug_print(depth + 1); 151 | } 152 | } 153 | ``` 154 | `debug_print()` の実装は以後省略. 155 | # Binary 156 | 二項演算子も同様に定義する. 157 | ```cpp:expression.hpp 158 | namespace expression { 159 | enum class BinaryOperator { 160 | Mul, Div, Rem, 161 | Add, Sub, 162 | LeftShift, RightShift, 163 | BitAnd, BitOr, BitXor, 164 | Equal, NotEqual, 165 | Less, Greater, 166 | LessEqual, GreaterEqual, 167 | LogicalAnd, LogicalOr, 168 | Assign, 169 | MulAssign, DivAssign, RemAssign, AddAssign, SubAssign, 170 | BitAndAssign, BitOrAssign, BitXorAssign, 171 | RightShiftAssign, LeftShiftAssign 172 | }; 173 | class Binary : public Expression { 174 | BinaryOperator binary_operator; 175 | std::unique_ptr left, right; 176 | public: 177 | Binary(BinaryOperator, std::unique_ptr, std::unique_ptr); 178 | void debug_print(int) const override; 179 | }; 180 | } 181 | ``` 182 | ```cpp:expression.cpp 183 | namespace expression { 184 | Binary::Binary( 185 | BinaryOperator binary_operator, 186 | std::unique_ptr left, 187 | std::unique_ptr right 188 | ): 189 | binary_operator(binary_operator), 190 | left(std::move(left)), 191 | right(std::move(right)) {} 192 | } 193 | ``` 194 | # Invocation 195 | 関数の呼び出しは,呼び出される関数と,引数の `std::vector` の組として表す. 196 | ```cpp:expression.hpp 197 | namespace expression { 198 | class Invocation : public Expression { 199 | std::unique_ptr function; 200 | std::vector> arguments; 201 | public: 202 | Invocation(std::unique_ptr, std::vector>); 203 | void debug_print(int) const override; 204 | }; 205 | } 206 | ``` 207 | ```cpp:expression.cpp 208 | namespace expression { 209 | Invocation::Invocation( 210 | std::unique_ptr function, 211 | std::vector> arguments 212 | ): 213 | function(std::move(function)), 214 | arguments(std::move(arguments)) {} 215 | } 216 | ``` -------------------------------------------------------------------------------- /books/llvm-toy-language/06-parse-expression.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "構文解析 (式)" 3 | --- 4 | 5 | 式はこんな感じの(なんちゃって)BNF で表される. 6 | ``` 7 | ::= 8 | | 9 | | 10 | | `(` `)` 11 | | `(` `)` 12 | ::= 13 | | 14 | ::= ε 15 | | 16 | | `,` 17 | ``` 18 | (どこがなんちゃってかというと,優先順位の話をしていない) 19 | 20 | これをパースする関数を作っていく. 21 | ```cpp:parser.hpp 22 | #include 23 | 24 | #include "lexer.hpp" 25 | #include "expression.hpp" 26 | 27 | std::unique_ptr parse_expression(Lexer &); 28 | ``` 29 | 30 | # factor 31 | まず,factor をパースする `parse_factor()` 関数から書いていく. 32 | 33 | `lexer` からトークンを 1 個 peek し,そいつに基づいて `|` で繋がれた 5 通りのうちのどれかを選択する. 34 | ```cpp:parser.cpp 35 | #include "parser.hpp" 36 | 37 | static std::unique_ptr parse_factor(lexer::Lexer &lexer){ 38 | auto &token_ref = lexer.peek(); 39 | 40 | // EOF に達した場合 41 | if(!token_ref) return nullptr; 42 | 43 | /* ここで factor を返す */ 44 | } 45 | ``` 46 | 47 | ## Identifier 48 | 1 個目のトークンが Identifier なら,1 つめの規則 49 | ``` 50 | ::= 51 | ``` 52 | が選ばれる. 53 | 54 | `token::Token` に,識別子ならその名前を返すような関数 `identifier()` を追加する. 55 | ```cpp:token.hpp 56 | #include 57 | 58 | namespace token { 59 | class Token { 60 | // 略 61 | public: 62 | virtual std::optional identifier(); 63 | }; 64 | 65 | class Identifier : public Token { 66 | // 略 67 | std::optional identifier() override; 68 | }; 69 | } 70 | ``` 71 | ```cpp:token.cpp 72 | namespace token { 73 | std::optional Token::identifier(){ 74 | return std::nullopt; 75 | } 76 | std::optional Identifier::identifier(){ 77 | return std::move(name); 78 | } 79 | } 80 | ``` 81 | これを用いて `parse_factor()` に 1 つめの規則を追加する. 82 | ```cpp:parser.cpp 83 | std::unique_ptr parse_factor(lexer::Lexer &lexer){ 84 | auto &token_ref = lexer.peek(); 85 | if(!token_ref) return nullptr; 86 | 87 | std::unique_ptr ret; 88 | pos::Range pos; 89 | if(auto name = token_ref->identifier()){ 90 | // peek した 1 個目のトークンは識別子だった. 91 | 92 | // peek していたものを読むと同時に,位置情報 pos をもらう 93 | pos = lexer.next()->pos; 94 | 95 | // expression::Identifier を作り,返り値に代入 96 | ret = std::make_unique(std::move(name).value()); 97 | }else{ 98 | // 残り 4 つの規則 99 | } 100 | 101 | // 位置情報を付与 102 | ret->pos = pos; 103 | 104 | return ret; 105 | } 106 | ``` 107 | 108 | ## Positive Integer 109 | 1 個目のトークンが Integer なら,2 つめの規則 110 | ``` 111 | ::= 112 | ``` 113 | が選ばれる. 114 | 115 | `token::Token` に,整数リテラルならその値を `std::int32_t` で返すような関数 `positive_integer()` を追加する. 116 | ```cpp:token.hpp 117 | #include 118 | 119 | namespace token { 120 | class Token { 121 | // 略 122 | public: 123 | virtual std::optional positive_integer(); 124 | }; 125 | 126 | class Integer : public Token { 127 | // 略 128 | std::optional positive_integer() override; 129 | }; 130 | } 131 | ``` 132 | オーバーフローの検出には Boost の Safe Numerics を使う. 133 | ```cpp:token.cpp 134 | #include 135 | 136 | #include "error.hpp" 137 | 138 | namespace token { 139 | using safe_i32 = boost::safe_numerics::safe; 140 | std::optional Token::positive_integer(){ 141 | return std::nullopt; 142 | } 143 | std::optional Integer::positive_integer(){ 144 | safe_i32 ret(0); 145 | constexpr safe_i32 base(10); 146 | try{ 147 | for(char c : value) ret = ret * base + safe_i32(c - '0'); 148 | }catch(std::exception &e){ 149 | throw error::make(e, pos); 150 | } 151 | return ret; 152 | } 153 | } 154 | ``` 155 | ただし `error::InvalidIntegerLiteral` は次のように定義. 156 | ```cpp:error.hpp 157 | namespace error { 158 | class InvalidIntegerLiteral : public Error { 159 | std::exception &error; 160 | pos::Range pos; 161 | public: 162 | InvalidIntegerLiteral(std::exception &, pos::Range); 163 | void eprint(const std::vector &) const override; 164 | }; 165 | } 166 | ``` 167 | 168 | これを用いて,上と同様に `parse_factor()` に 2 個目の規則を追加. 169 | ```cpp:parser.cpp 170 | std::unique_ptr parse_factor(lexer::Lexer &lexer){ 171 | auto &token_ref = lexer.peek(); 172 | if(!token_ref) return nullptr; 173 | 174 | std::unique_ptr ret; 175 | pos::Range pos; 176 | if(auto name = token_ref->identifier()){ 177 | // 略 178 | }else if(auto value = token_ref->positive_integer()){ 179 | // 1 個目のトークンは整数リテラルだった 180 | pos = lexer.next()->pos; 181 | ret = std::make_unique(value.value()); 182 | }else{ 183 | // 残りの規則 184 | } 185 | ret->pos = pos; 186 | return ret; 187 | } 188 | ``` 189 | ## prefix 190 | 1 個目のトークンが Integer なら,3 つめの規則 191 | ``` 192 | ::= 193 | ``` 194 | が選ばれる. 195 | 196 | `token::Token` に,前置演算子なら対応する `expression::UnaryOperator` を返すような関数 `prefix()` を追加する. 197 | 198 | ```cpp:token.hpp 199 | #include "expression.hpp" 200 | 201 | namespace token { 202 | class Token { 203 | // 略 204 | public: 205 | virtual std::optional prefix(); 206 | } 207 | class Plus : public Token { 208 | // 略 209 | std::optional prefix() override; 210 | }; 211 | class Hyphen : public Token { 212 | // 略 213 | std::optional prefix() override; 214 | }; 215 | class Tilde : public Token { 216 | // 略 217 | std::optional prefix() override; 218 | }; 219 | class Exclamation : public Token { 220 | // 略 221 | std::optional prefix() override; 222 | }; 223 | } 224 | ``` 225 | ```cpp:token.cpp 226 | namespace token { 227 | std::optional Token::prefix(){ 228 | return std::nullopt; 229 | } 230 | std::optional Plus::prefix(){ 231 | return expression::UnaryOperator::Plus; 232 | } 233 | std::optional Hyphen::prefix(){ 234 | return expression::UnaryOperator::Minus; 235 | } 236 | std::optional Tilde::prefix(){ 237 | return expression::UnaryOperator::BitNot; 238 | } 239 | std::optional Exclamation::prefix(){ 240 | return expression::UnaryOperator::LogicalNot; 241 | } 242 | } 243 | ``` 244 | 245 | ここで,演算子の位置情報とオペランドの位置情報を合わせて式全体の位置情報とする必要がある.そこで,`pos::Range` に次のような演算子をオーバーロードする. 246 | 247 | ```cpp:pos.hpp 248 | namespace pos { 249 | class Range { 250 | // 略 251 | public: 252 | Range &operator+=(const Range &); 253 | }; 254 | } 255 | ``` 256 | ```cpp:pos.cpp 257 | namespace pos { 258 | Range &Range::operator+=(const Range &other){ 259 | end = other.end; 260 | return *this; 261 | } 262 | } 263 | ``` 264 | すると,`parse_factor` の 3 個目の規則は次のように書ける. 265 | ```cpp:parse.cpp 266 | std::unique_ptr parse_factor(lexer::Lexer &lexer){ 267 | auto &token_ref = lexer.peek(); 268 | if(!token_ref) return nullptr; 269 | std::unique_ptr ret; 270 | pos::Range pos; 271 | if(auto name = token_ref->identifier()){ 272 | // 略 273 | }else if(auto value = token_ref->positive_integer()){ 274 | // 略 275 | }else if(auto prefix = token_ref->prefix()){ 276 | // 演算子の位置 277 | pos = lexer.next()->pos; 278 | 279 | // parse_factor() を再帰呼出ししてオペランドを得る 280 | auto operand = parse_factor(lexer); 281 | 282 | // オペランドの位置情報を合わせて,全体の位置情報を得る 283 | pos += operand->pos; 284 | 285 | ret = std::make_unique(prefix.value(), std::move(operand)); 286 | }else{ 287 | // 残りの規則 288 | } 289 | ret->pos = pos; 290 | return ret; 291 | } 292 | ``` 293 | ただし,これだと -2147483648 がエラーになる. 294 | ``` 295 | > -2147483648 296 | invalid integer literal (addition result too large: positive overflow error) at 1:2-1:11 297 | - !-> 2147483648 <-! 298 | ``` 299 | よって,`-` の直後に整数リテラルが来る場合のみ別にする.`token::Token::positive_integer()` と同様に今度は `token::Token::negative_integer()` を用意して, 300 | ```cpp:token.hpp 301 | namespace token { 302 | class Token { 303 | /* 略 */ 304 | public: 305 | virtual std::optional negative_integer(); 306 | }; 307 | 308 | class Integer : public Token { 309 | /* 略 */ 310 | public: 311 | std::optional negative_integer() override; 312 | }; 313 | } 314 | ``` 315 | ```cpp:token.cpp 316 | namespace token { 317 | std::optional Token::negative_integer(){ 318 | return std::nullopt; 319 | } 320 | std::optional Integer::negative_integer(){ 321 | safe_i32 ret(0); 322 | constexpr safe_i32 base(10); 323 | try{ 324 | for(char c : value) ret = ret * base - safe_i32(c - '0'); 325 | }catch(std::exception &e){ 326 | throw error::make(e, pos); 327 | } 328 | return ret; 329 | } 330 | } 331 | ``` 332 | `-` の直後に整数リテラルが来るときのみこれを使うようにする. 333 | ```cpp:parse.cpp 334 | std::unique_ptr parse_factor(lexer::Lexer &lexer){ 335 | auto &token_ref = lexer.peek(); 336 | if(!token_ref) return nullptr; 337 | std::unique_ptr ret; 338 | pos::Range pos; 339 | if(auto name = token_ref->identifier()){ 340 | // 略 341 | }else if(auto value = token_ref->positive_integer()){ 342 | // 略 343 | }else if(auto prefix = token_ref->prefix()){ 344 | pos = lexer.next()->pos; 345 | if( 346 | prefix.value() == expression::UnaryOperator::Minus 347 | && (value = lexer.peek()->negative_integer()) 348 | ){ 349 | // 整数リテラルを peek 中 350 | 351 | // 整数リテラル(オペランド)の位置情報を追加 352 | pos += lexer.next()->pos; 353 | 354 | ret = std::make_unique(value.value()); 355 | }else{ 356 | auto operand = parse_factor(lexer); 357 | pos += operand->pos; 358 | ret = std::make_unique(prefix.value(), std::move(operand)); 359 | } 360 | } 361 | ret->pos = pos; 362 | return ret; 363 | } 364 | ``` 365 | -------------------------------------------------------------------------------- /books/llvm-toy-language/config.yaml: -------------------------------------------------------------------------------- 1 | title: "LLVM 初心者が JIT コンパイラに挑戦" 2 | summary: "挑戦してみます." 3 | topics: [cpp, llvm] 4 | published: true 5 | price: 0 6 | chapters: 7 | - 01-intro 8 | - 02-error 9 | - 03-token 10 | - 04-lexer 11 | - 05-expression 12 | - 06-parse-expression 13 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/01-intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "序" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/02-environment-setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "環境構築" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/03-hello-world.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Hello, world!" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/04-arithmetic.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "算術演算" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/05-variable.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "変数と型" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/06-literal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "リテラル" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/07-input.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "入力の受け取り" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/08-if.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "if 式" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/09-block.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ブロックとスコープ" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/10-assert.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "アサート" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/11-tuple.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "タプル" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/12-array.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "配列" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/13-format.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "フォーマット出力" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/14-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "参照とライフタイム" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/15-for.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "for 式" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/16-pattern.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "パターンマッチ" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/17-mutable.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "可変変数" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/18-mutable-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "可変参照" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/19-vector.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ベクタ" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/20-loop.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "様々なループ" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/21-slice.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "スライス" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/22-match.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "パターンマッチと条件分岐" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/23-string.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "文字列" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/24-ownership.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "所有権" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/25-function.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "関数" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/26-call-by-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "参照渡し" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/27-boolean.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "演算子と bool 型" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/28-recursion.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "再帰関数" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/29-bitwise-operation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ビット演算" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/30-time-complexity.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "時間計算量" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/31-generic-function.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "関数のジェネリクス" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/32-returning-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "参照を返す関数" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/33-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "構造体" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/34-enumerated-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "列挙型" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/35-method.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "メソッド" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/36-generic-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "構造体と列挙型のジェネリクス" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/37-option.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Option" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/38-result.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Result" 3 | --- 4 | 5 | [『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』序](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)をご覧ください。 6 | -------------------------------------------------------------------------------- /books/rust-atcoder-old/config.yaml: -------------------------------------------------------------------------------- 1 | title: "RustCoder 避難所" 2 | summary: "RustCoder ( https://zenn.dev/toga/books/rust-atcoder ) の改修が終わるまで,古い内容を一時的にこちらに移しています." 3 | topics: ["rust", "競技プログラミング", "atcoder"] 4 | published: true 5 | price: 0 6 | chapters: 7 | - 01-intro 8 | - 02-environment-setup 9 | - 03-hello-world 10 | - 04-arithmetic 11 | - 05-variable 12 | - 06-literal 13 | - 07-input 14 | - 08-if 15 | - 09-block 16 | - 10-assert 17 | - 11-tuple 18 | - 12-array 19 | - 13-format 20 | - 14-reference 21 | - 15-for 22 | - 16-pattern 23 | - 17-mutable 24 | - 18-mutable-reference 25 | - 19-vector 26 | - 20-loop 27 | - 21-slice 28 | - 22-match 29 | - 23-string 30 | - 24-ownership 31 | - 25-function 32 | - 26-call-by-reference 33 | - 27-boolean 34 | - 28-recursion 35 | - 29-bitwise-operation 36 | - 30-time-complexity 37 | - 31-generic-function 38 | - 32-returning-reference 39 | - 33-structure 40 | - 34-enumerated-type 41 | - 35-method 42 | - 36-generic-structure 43 | - 37-option 44 | - 38-result -------------------------------------------------------------------------------- /books/rust-atcoder/01.intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "序" 3 | --- 4 | 5 | 『RustCoder ―― AtCoder と Rust で始める競技プログラミング入門』は、私が2020年12月に [C++入門 AtCoder Programming Guide for beginners (APG4b)](https://atcoder.jp/contests/APG4b) の Rust 版として書き始め、その後3年以上にわたって少しずつ加筆修正を繰り返してきたものです。丁寧な Rust 入門書として多くの方から好評をいただきました。 6 | 7 | 一方、Rust は常に変化し続ける言語です。私は少しでも正確で新しい情報をやさしく伝えられるように頑張っていましたが、本業との両立が難しくなり、ここ 1 年ほとんど更新できずにいました。自分の書いた情報が古くなっていく様子を眺めるのは苦痛でした。 8 | 9 | しかし、記事を消すことはできずにいました。世の中は Rust に対する誤解で溢れており、その多くは他言語からの誤った類推です。前提知識を捨て去ってプログラミング初心者の目線から整理しないと、正しい理解には至れません。*できる人*こそ多けれど、*やる人*は少ない作業です。誰もやらないなら、私がやるしかないと思っていました。 10 | 11 | しかし、その重荷から解放してくれる技術が登場しました。**LLM** です。正直、あまりに目まぐるしくて追い付けないのですが、どうやら GPT o1, o3 や Deep Research の登場により、正確で分かりやすい説明が次々と得られるようです。その証拠に、ブログサービスは今や LLM による「良質な」記事で溢れています。 12 | 13 | そこで、時間が止まってしまった私の記事は取り下げ、代わりに同様の内容を LLM に解説してもらうためのプロンプトを掲載します。 14 | 15 | この記事を書いたことは私にとって貴重な経験で、何より楽しみ、喜びでした。今までご感想やご指摘など様々なフィードバックを下さった皆様に感謝申し上げます。 16 | -------------------------------------------------------------------------------- /books/rust-atcoder/02.setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "環境構築" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。まずは環境構築を教えて下さい。私の環境について情報が不足していたら質問してください。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/03.hello-world.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Hello, world!" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。環境構築は済んだので、まずはHello worldの仕組みを教えて下さい。コンパイル時と実行時にそれぞれ何が起こるのでしょうか? 9 | ``` 10 | 11 | ``` 12 | コンパイルエラーが出たらどうすればいいでしょうか? 13 | ``` 14 | -------------------------------------------------------------------------------- /books/rust-atcoder/04.arithmetic.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "算術演算" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。Hello worldは実行できたので、println!マクロを用いて整数や小数を出力する方法を教えて下さい。コンパイル時と実行時にそれぞれ何が起こるのでしょうか? 9 | ``` 10 | 11 | ``` 12 | 四則演算についても教えて下さい。 13 | ``` 14 | -------------------------------------------------------------------------------- /books/rust-atcoder/05.variable.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "変数と型" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。Hello worldは実行できたので、変数と型について教えて下さい。コンパイル時と実行時にそれぞれ何が起こるのでしょうか? 9 | ``` 10 | 11 | ``` 12 | 未初期化の変数はどんな状態ですか? 13 | ``` 14 | -------------------------------------------------------------------------------- /books/rust-atcoder/06.literal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "リテラル" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。文字列リテラル、整数リテラル、浮動小数点数リテラルは、それぞれどんな書き方ができるのでしょうか? 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/07.input.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "入力の受け取り" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は簡単な標準出力と変数の使い方を学んだところです。今回は、`proconio::input!`を使った入力の受取りを教えて下さい。コンパイル時と実行時にそれぞれ何が起こるのでしょうか? 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/08.if.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "if 式" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は簡単な標準入出力と変数の使い方を学んだところです。今回は、`if`式について教えて下さい。コンパイル時と実行時にそれぞれ何が起こるのでしょうか? 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/09.block.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ブロックとスコープ" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は簡単な標準入出力と変数の使い方を学んだところです。今回は、変数のスコープについて教えて下さい。コンパイル時と実行時にそれぞれ何が起こるのでしょうか? 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/10.assert.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "アサート" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。`assert!`マクロとは何ですか? 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/11.tuple.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "タプル" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。タプルとは何ですか? 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/12.array.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "配列" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。配列とは何ですか? 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/13.format.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "フォーマット出力" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。今回は、`println!` マクロのフォーマット文字列について詳しく教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/14.reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "参照とライフタイム" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は簡単な変数の使い方とスコープについて学んだところです。今回は不変参照とそのライフタイムについて教えて下さい。プログラムが動く仕組みが何も分からない私でも理解できるよう、コンパイル時と実行時にそれぞれ何が起こるかやさしく説明してください。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/15.for.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "for 式" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は配列と不変参照について学んだところです。次は配列を`for`式で走査する方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/16.pattern.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "パターンマッチ" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私に、Rustを教えてください。以下のコードについて詳しく教えて下さい。 9 | fn main() { 10 | let elements: [(i32, f64); 5] = [(6, 12.0), (7, 14.0), (8, 16.0), (15, 31.0), (16, 32.1)]; 11 | for &(number, weight) in &elements { 12 | println!("{}: {:.1}", number, weight); 13 | } 14 | } 15 | ``` 16 | 17 | ``` 18 | 次との違いは何ですか? 19 | fn main() { 20 | let elements: [(i32, f64); 5] = [(6, 12.0), (7, 14.0), (8, 16.0), (15, 31.0), (16, 32.1)]; 21 | for (number, weight) in &elements { 22 | println!("{}: {:.1}", number, weight); 23 | } 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /books/rust-atcoder/17.mutable.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "可変変数" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は配列、不変参照、for式について学んだところで、mutについてはまだ何も知りません。今回は、与えられた配列の総和をfor式で計算する方法を教えて下さい。 9 | ``` 10 | 11 | ``` 12 | mutについて詳しく教えて下さい。プログラムが動く仕組みが何も分からない私でも理解できるよう、コンパイル時と実行時にそれぞれ何が起こり、何が可変で何が不変かやさしく説明して下さい。 13 | ``` 14 | -------------------------------------------------------------------------------- /books/rust-atcoder/18.mutable-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "可変参照" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は不変参照とそのライフタイムについて学んだところです。今回は、可変参照とそのライフタイムについて教えて下さい。 9 | ``` 10 | 11 | ``` 12 | 可変参照のルールの理由を教えて下さい。 13 | ``` 14 | -------------------------------------------------------------------------------- /books/rust-atcoder/19.loop.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "様々なループ" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は単純なfor式について学んだところです。今回は、他のループやbreak, continueについて教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/20.label.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ループのラベルと値返し" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今はfor式について学んだところです。今回は、二重ループを抜ける方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/21.function.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "関数" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、関数を定義して使う方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/22.crate.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "クレートとパス" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、クレートについて具体例を挙げながら教えて下さい。 9 | ``` 10 | 11 | ``` 12 | useについてもっと詳しく教えて下さい。 13 | ``` 14 | -------------------------------------------------------------------------------- /books/rust-atcoder/23.recursion.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "再帰関数とスタック領域" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、再帰関数について具体例を挙げながら教えて下さい。 9 | ``` 10 | 11 | ``` 12 | 再帰関数におけるメモリの使われ方について、プログラムが動く仕組みが何も分からない私でも理解できるようにやさしく説明して下さい。 13 | ``` 14 | -------------------------------------------------------------------------------- /books/rust-atcoder/24.vector.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ベクタ" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、Vecの基本的な使い方を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/25.ownership.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "所有権" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今はちょうどVecの基本的な使い方を学んだところです。プログラムが動く仕組みが何も分からない私でも所有権の概念が理解できるよう、ヒープアロケーションについて丁寧に教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/26.slice.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "スライス" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今は配列とVecを学んだところです。今回は、スライス[T]の基本的な使い方を教えて下さい。実行時に何が起こっているか分かりやすく説明してください。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/27.call-by-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "参照渡し" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。所有権と参照について学んだので、参照渡しを教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/28.match.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "パターンマッチと条件分岐" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、if let式、while let式、match式について教えて下さい。構造体や列挙体(ResultやOption)はまだ知らないので、代わりにスライスを使って説明して下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/29.string.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "文字列" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回はUTF-8とString, strについて教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/30.boolean.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "演算子と bool 型" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回はboolについて教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/31.bitwise-operation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ビット演算" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回はビット演算について教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/32.time-complexity.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "時間計算量" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | 時間計算量について、具体例を挙げながら教えて下さい。ランダウのOの正確な定義に気を付けて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/33.generic-function.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "関数のジェネリクス" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は関数のジェネリクスについて教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/34.returning-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "参照を返す関数" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は関数のライフタイムパラメータについて教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/35.structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "構造体" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、構造体を定義して使う方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/36.enumerated-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "列挙型" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、列挙型を定義して使う方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/37.method.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "メソッド" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、メソッドと関連関数を定義して使う方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/38.generic-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "構造体と列挙型のジェネリクス" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、ジェネリックな構造体や列挙型を定義して使う方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/39.option.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Option" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、Optionの定義と基本的な使い方を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/40.result.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Result" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、Resultの定義と基本的な使い方を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/41.trait.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "トレイト" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、トレイトを定義して使う方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/42.struct-with-reference.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "参照をフィールドにもつ構造体" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、参照をフィールドにもつ構造体を定義して使う方法を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/43.closure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "クロージャ" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、クロージャを教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/44.iterator.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "イテレータ" 3 | --- 4 | 5 | 私の記事は古くなっていくので[(説明)](https://zenn.dev/toga/books/rust-atcoder/viewer/intro)、代わりに以下のプロンプトを最新の LLM に渡して下さい: 6 | 7 | ``` 8 | あなたは分かりやすくて丁寧なプログラミングの先生です。プログラミングの経験が全く無い私は、あなたにRustを教わっています。今回は、イテレータの基本的な使い方を教えて下さい。 9 | ``` 10 | -------------------------------------------------------------------------------- /books/rust-atcoder/config.yaml: -------------------------------------------------------------------------------- 1 | title: "RustCoder ―― AtCoder と Rust で始める競技プログラミング入門" 2 | summary: "AtCoder のジャッジシステムを利用しながらプログラミング言語 Rust について解説します.C++ や python のような他の言語に触れたことのないプログラミング初心者でも読めるよう努めています.更新履歴は github ( https://github.com/fiveseven-lambda/zenn/commits/main/books/rust-atcoder ) から見られます.内容に関するご意見やご指摘などは,プルリクエストか twitter ( https://twitter.com/57tggx ) で遠慮なくお願いします.カバー画像: GRAPHY 筧様" 3 | topics: ["rust", "競技プログラミング", "atcoder"] 4 | published: true 5 | price: 0 -------------------------------------------------------------------------------- /books/rust-atcoder/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/books/rust-atcoder/cover.png -------------------------------------------------------------------------------- /images/array_reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/array_reference.png -------------------------------------------------------------------------------- /images/heap-allocate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/heap-allocate.png -------------------------------------------------------------------------------- /images/heap-allocator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/heap-allocator.png -------------------------------------------------------------------------------- /images/heap-deallocate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/heap-deallocate.png -------------------------------------------------------------------------------- /images/option-path-dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/option-path-dp.png -------------------------------------------------------------------------------- /images/option-path-dp2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/option-path-dp2.png -------------------------------------------------------------------------------- /images/option-path-dp3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/option-path-dp3.png -------------------------------------------------------------------------------- /images/option-path-dp4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/option-path-dp4.png -------------------------------------------------------------------------------- /images/reference_memory_1byte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/reference_memory_1byte.png -------------------------------------------------------------------------------- /images/reference_memory_4byte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/reference_memory_4byte.png -------------------------------------------------------------------------------- /images/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/stack.png -------------------------------------------------------------------------------- /images/stack_function_call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiveseven-lambda/zenn/e042a94442a23cacf24ed9577e50125ed3fe43dd/images/stack_function_call.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zenn", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "zenn", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "zenn-cli": "^0.1.158" 12 | } 13 | }, 14 | "node_modules/zenn-cli": { 15 | "version": "0.1.158", 16 | "resolved": "https://registry.npmjs.org/zenn-cli/-/zenn-cli-0.1.158.tgz", 17 | "integrity": "sha512-bvQ96fK+9qw+SKxEmncg/rYU+1u0V6Bh+HuV8By+u4L+VZt/jn9hFyL+KWj/2ncSBDtcEBo6XLsCdNLxlUo+RA==", 18 | "license": "MIT", 19 | "bin": { 20 | "zenn": "dist/server/zenn.js" 21 | }, 22 | "engines": { 23 | "node": ">=14.0.0" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zenn", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "zenn-cli": "^0.1.158" 6 | } 7 | } 8 | --------------------------------------------------------------------------------