0
部分文字列の取得を   効率よく!楽に!~fundoshi.hppの紹介と今後の予定~H.Hiro (Maraigue) @札幌C++勉強会          main@hhiro.net         Twitter: @h_hiro_
自己紹介H.Hiro
自己紹介H.Hiro• とりあえず  Twitterの@h_hiro_か  githubのmaraigue(まれーぐ)をご覧下さい
以上
すみません端折りすぎました
自己紹介H.Hiro• 大学院生やってます(情報系)• 趣味でもプログラム書いてます プログラミング歴15年(ただし、職業プログラミング歴は無し)
自己紹介H.Hiro• 大学院生やってます(情報系)• 趣味でもプログラム書いてます• メインはRuby 札幌Ruby会議2012(9月14日~16日)のスタッフでした http://gihyo.jp/news/report/01/sappor...
自己紹介H.Hiro•   大学院生やってます(情報系)•   趣味でもプログラム書いてます•   メインはRuby•   C++は研究での利用がメイン     その中から汎用的に使えそうな部分を      切り出したのがfundoshi.h...
fundoshi.hppとは
その前に
部分文字列とは
部分文字列とは文字列xに対し、xのi文字目, (i+1)文字目, ...,j文字目(0≦i≦j<x.length)からなる文字列をxの部分文字列(substring)という。std::string x("Sapporo.cpp");std::s...
部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "ap...
部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "ap...
部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "ap...
部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "ap...
部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;      ...
メモリ消費の問題を回避するいちいちコピーすると、メモリも時間も余計に必要メモリ(RAM)  Sapporo.cpp    appor  ↑         ↑    ↑   ↑  xの起点   xの終点 yの起点   yの終点
メモリ消費の問題を回避するいちいちコピーすると、メモリも時間も余計に必要           ↓じゃあコピーしないでメモリを共有すればいい(ただし、xの内容が更新されない場合に限る)yの起点     yの終点   ↓メモリ(RAM) ↓  Sa...
メモリ消費の問題を回避するC#ユーザ   Rubyユーザ   Pythonユーザ そんな面倒なことしないよー。 メンテナンス性も悪くなるし。
メモリ消費の問題を回避するC#ユーザ   Rubyユーザ   Pythonユーザ   C++ユーザ            ポインタ使って、無駄なメモリ            使わないようにすればいいさ。               面倒だけどね…。
メモリ消費の問題を回避するC#ユーザ    Rubyユーザ   Pythonユーザ   C++ユーザH.Hiro            じゃあ面倒じゃなくしてやるよ!             そのためのクラス作ってな!
それでは改めてfundoshi.hppとは
fundoshi.hppとは部分文字列をメモリを共有したまま取得するライブラリstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::...
fundoshi.hppとはstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[...
fundoshi.hppとは std::string x("Sapporo.cpp"); fundoshi::string y; y = fundoshi::string(x, 1, 5); std::cout << y << std::end...
fundoshi.hppとはstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[...
fundoshi.hppとはstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[...
fundoshi.hppとはstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[...
ところで
何でfundoshi なのか
fundoshiって名前の理由• 「他人のふんどしで相撲を取る」  が直接の由来• ふんどしって長いものだから  stringに合ってるよねてことで採用                                           Pho...
fundoshi.hppの     特徴
fundoshi.hppの制限事項• 元になる文字列の内容を書き換えると  fundoshi側でも内容が書き換わります• fundoshi側から文字列の内容を書き換える  ことはできません  (const char * みたいなもの) std:...
fundoshi.hppの特長(1/2)std::string(C++標準のstring)と同じ感覚で使える• 自前でポインタを持つのと違って、  文字列長の情報とかも一緒にクラス内に  入っている。lengthメソッドとかもある。
fundoshi.hppの特長(1/2) std::string(C++標準のstring)と同じ感覚で 使える • メソッドのAPIは基本的に合わせてますconst char *p = "sapporo.cpp";fundoshi::stri...
fundoshi.hppの特長(1/2) std::string(C++標準のstring)と同じ感覚で 使える • メソッドのAPIは基本的に合わせてます • std::basic_string<T> に相当するものとして   fundosh...
fundoshi.hppの特長(2/2)自前で関数を作るとき引数の型をfundoshi::stringとしておけばstd::stringも受け取れるint count_space(const fundoshi::string & s){    ...
実演
使ってみよう"abcdefghijklmnopqrstuvwxyz"の空ではないすべての部分文字列を列挙する("a", "bcd", "defghijkl", ...)• std::stringだと ???byte• fundoshi::str...
使ってみよう"abcdefghijklmnopqrstuvwxyz"の空ではないすべての部分文字列を列挙する("a", "bcd", "defghijkl", ...)• std::stringだと 3276byte• fundoshi::st...
使ってみよう"abcdefghijklmnopqrstuvwxyz"の空ではないすべての部分文字列を列挙する("a", "bcd", "defghijkl", ...)• std::stringだと 3276byte n(n+1)(n+2)÷6...
使ってみよう"abcdefghijklmnopqrstuvwxyz"の空ではないすべての部分文字列を列挙する("a", "bcd", "defghijkl", ...)• std::stringだと 3276byte n(n+1)(n+2)÷6...
応用例
使い方(1/3)• もともとは、いろんな文字列アルゴリズムを  実装するのに欲しくなったので作った
使い方(1/3)• もともとは、いろんな文字列アルゴリズムを  実装するのに欲しくなったので作った問題:文字列tから最速で部分文字列を検索するための(曖昧検索等は考慮しなくてよい)索引は?
使い方(1/3)• もともとは、いろんな文字列アルゴリズムを  実装するのに欲しくなったので作った問題:文字列tから最速で部分文字列を検索するための(曖昧検索等は考慮しなくてよい)索引は?答え:tの部分文字列を全部格納しておく
使い方(1/3)• もともとは、いろんな文字列アルゴリズムを  実装するのに欲しくなったので作ったSuffix Tree           C         ←"CACAO"の                          A O   ...
使い方(1/3)• もともとは、いろんな文字列アルゴリズムを  実装するのに欲しくなったので作ったSuffix Tree           C         ←"CACAO"の                          A O   ...
使い方(1/3)• もともとは、いろんな文字列アルゴリズムを  実装するのに欲しくなったので作ったSuffix Tree           C         ←"CACAO"の                          A O   ...
使い方(1/3) • もともとは、いろんな文字列アルゴリズムを   実装するのに欲しくなったので作った                        fundoshi(4,1) Suffix Tree        fundoshi(1,1) ...
使い方(1/3) • もともとは、いろんな文字列アルゴリズムを   実装するのに欲しくなったので作った                          fundoshi(4,1) Suffix Tree        fundoshi(1,1...
使い方(2/3)  Webアプリケーションとかでも便利と思われる  例) 7文字を超えた部分を切り詰めて     「...」に置き換えたい  「本当にありがとうございました」→「本当にありがと...」//普通の方法if(buf.length()...
使い方(2/3)  Webアプリケーションとかでも便利と思われる  例) 7文字を超えた部分を切り詰めて     「...」に置き換えたい                       メモリがコピー  「本当にありがとうございました」→「本当に...
使い方(2/3)  Webアプリケーションとかでも便利と思われる  例) 7文字を超えた部分を切り詰めて     「...」に置き換えたい  「本当にありがとうございました」→「本当にありがと...」//fundoshi.hppを使うとif(b...
使い方(3/3)Webアプリの裏方(フレームワークやWebサーバなど)でも便利と思われるSERVER_QUERY="hoge=11-17&piyo=NAME"              ~~~~ ~~~~~ ~~~~ ~~~~         ...
今後の展望
今後の展望(1/2)必然的に部分文字列を取る関数はfundoshi::stringを返すと幸せになれるものが多いはず例) boost::match_results(正規表現などでの  検索結果の文字列を示すクラス)は  std::stringを...
今後の展望(2/2)ファイルストレージにも同様に適用できないか• 現状はRAM上の文字列にしか適用できない• 大容量のファイルを扱う場合などに便利• SSDなら読み込み時間の影響も少ない
おわりに
おわりに(1/2)fundoshi.hppのソースコードhttp://github.com/maraigue/fundoshi.hpp今回のデモ(部分文字列列挙)のソースコードhttp://github.com/maraigue/       ...
おわりに(2/2)「部分文字列を取得するにあたって、メモリ余分に喰ってないか?」と思ったらfundoshi.hppのことを思い出して頂けると幸いです
ありがとうございました
Upcoming SlideShare
Loading in...5
×

2012.11.17 CLR/H&札幌C++勉強会 発表資料「部分文字列の取得を 効率よく!楽に! - fundoshi.hppの紹介と今後の予定 -」

1,221

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,221
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
2
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Transcript of "2012.11.17 CLR/H&札幌C++勉強会 発表資料「部分文字列の取得を 効率よく!楽に! - fundoshi.hppの紹介と今後の予定 -」"

  1. 1. 部分文字列の取得を 効率よく!楽に!~fundoshi.hppの紹介と今後の予定~H.Hiro (Maraigue) @札幌C++勉強会 main@hhiro.net Twitter: @h_hiro_
  2. 2. 自己紹介H.Hiro
  3. 3. 自己紹介H.Hiro• とりあえず Twitterの@h_hiro_か githubのmaraigue(まれーぐ)をご覧下さい
  4. 4. 以上
  5. 5. すみません端折りすぎました
  6. 6. 自己紹介H.Hiro• 大学院生やってます(情報系)• 趣味でもプログラム書いてます プログラミング歴15年(ただし、職業プログラミング歴は無し)
  7. 7. 自己紹介H.Hiro• 大学院生やってます(情報系)• 趣味でもプログラム書いてます• メインはRuby 札幌Ruby会議2012(9月14日~16日)のスタッフでした http://gihyo.jp/news/report/01/sapporo-rubykaigi2012
  8. 8. 自己紹介H.Hiro• 大学院生やってます(情報系)• 趣味でもプログラム書いてます• メインはRuby• C++は研究での利用がメイン  その中から汎用的に使えそうな部分を 切り出したのがfundoshi.hppです
  9. 9. fundoshi.hppとは
  10. 10. その前に
  11. 11. 部分文字列とは
  12. 12. 部分文字列とは文字列xに対し、xのi文字目, (i+1)文字目, ...,j文字目(0≦i≦j<x.length)からなる文字列をxの部分文字列(substring)という。std::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "appor"を表示
  13. 13. 部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "appor"を表示これの内部処理は?
  14. 14. 部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "appor"を表示メモリ(RAM)
  15. 15. 部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "appor"を表示メモリ(RAM) Sapporo.cpp ↑ ↑ xの起点 xの終点
  16. 16. 部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl;// "appor"を表示メモリ(RAM) Sapporo.cpp appor ↑ ↑ ↑ ↑ xの起点 xの終点 yの起点 yの終点
  17. 17. 部分文字列とはstd::string x("Sapporo.cpp");std::string y = x.substr(1, 5);// xの1文字目から5文字取得するので…std::cout << y << std::endl; •文字数ぶんだけメモリを消費// "appor"を表示 •内容のコピーもしないとならないメモリ(RAM) Sapporo.cpp appor ↑ ↑ ↑ ↑ xの起点 xの終点 yの起点 yの終点
  18. 18. メモリ消費の問題を回避するいちいちコピーすると、メモリも時間も余計に必要メモリ(RAM) Sapporo.cpp appor ↑ ↑ ↑ ↑ xの起点 xの終点 yの起点 yの終点
  19. 19. メモリ消費の問題を回避するいちいちコピーすると、メモリも時間も余計に必要 ↓じゃあコピーしないでメモリを共有すればいい(ただし、xの内容が更新されない場合に限る)yの起点 yの終点 ↓メモリ(RAM) ↓ Sapporo.cpp ↑ ↑ xの起点 xの終点
  20. 20. メモリ消費の問題を回避するC#ユーザ Rubyユーザ Pythonユーザ そんな面倒なことしないよー。 メンテナンス性も悪くなるし。
  21. 21. メモリ消費の問題を回避するC#ユーザ Rubyユーザ Pythonユーザ C++ユーザ ポインタ使って、無駄なメモリ 使わないようにすればいいさ。 面倒だけどね…。
  22. 22. メモリ消費の問題を回避するC#ユーザ Rubyユーザ Pythonユーザ C++ユーザH.Hiro じゃあ面倒じゃなくしてやるよ! そのためのクラス作ってな!
  23. 23. それでは改めてfundoshi.hppとは
  24. 24. fundoshi.hppとは部分文字列をメモリを共有したまま取得するライブラリstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[5] = R;std::cout << y << std::endl;
  25. 25. fundoshi.hppとはstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[5] = R;std::cout << y << std::endl; Sapporo.cpp ↑ ↑ xの起点 xの終点
  26. 26. fundoshi.hppとは std::string x("Sapporo.cpp"); fundoshi::string y; y = fundoshi::string(x, 1, 5); std::cout << y << std::endl; x[5] = R;yの起点 yの終点 std::endl; std::cout << y << ↓ ↓ Sapporo.cpp ↑ ↑ xの起点 xの終点
  27. 27. fundoshi.hppとはstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[5] = R;std::cout << y << std::endl; “appor”を表示 Sapporo.cpp ↑ ↑ xの起点 xの終点
  28. 28. fundoshi.hppとはstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[5] = R;std::cout << y << std::endl; SappoRo.cpp ↑ ↑ xの起点 xの終点
  29. 29. fundoshi.hppとはstd::string x("Sapporo.cpp");fundoshi::string y;y = fundoshi::string(x, 1, 5);std::cout << y << std::endl;x[5] = R;std::cout << y << std::endl; SappoRo.cpp ↑ ↑ “appoR”を表示 xの起点 xの終点
  30. 30. ところで
  31. 31. 何でfundoshi なのか
  32. 32. fundoshiって名前の理由• 「他人のふんどしで相撲を取る」 が直接の由来• ふんどしって長いものだから stringに合ってるよねてことで採用 Photo by KAIZUKA Creative Commons BY-NC-SA 2.0 http://www.flickr.com/photos/kaizuka/386511394/
  33. 33. fundoshi.hppの 特徴
  34. 34. fundoshi.hppの制限事項• 元になる文字列の内容を書き換えると fundoshi側でも内容が書き換わります• fundoshi側から文字列の内容を書き換える ことはできません (const char * みたいなもの) std::string x("Sapporo.cpp"); fundoshi::string y; y = fundoshi::string(x, 1, 5); x[5] = R; // y[4] = R; // 不可!
  35. 35. fundoshi.hppの特長(1/2)std::string(C++標準のstring)と同じ感覚で使える• 自前でポインタを持つのと違って、 文字列長の情報とかも一緒にクラス内に 入っている。lengthメソッドとかもある。
  36. 36. fundoshi.hppの特長(1/2) std::string(C++標準のstring)と同じ感覚で 使える • メソッドのAPIは基本的に合わせてますconst char *p = "sapporo.cpp";fundoshi::string y(p, 7); // std::string y(p, 7); に相当y.length(); // std::stringのlengthと同様y[4]; //std::stringの[]演算子と同様y.begin(); // イテレータも取れる
  37. 37. fundoshi.hppの特長(1/2) std::string(C++標準のstring)と同じ感覚で 使える • メソッドのAPIは基本的に合わせてます • std::basic_string<T> に相当するものとして fundoshi::basic_string<T> がありますstd::basic_string<int> hoge; // 各文字がintで表されるような文字列fundoshi::basic_string<int> piyo; // fundoshi版
  38. 38. fundoshi.hppの特長(2/2)自前で関数を作るとき引数の型をfundoshi::stringとしておけばstd::stringも受け取れるint count_space(const fundoshi::string & s){ int result = 0; for(size_t i = 0; i < s.length(); ++i){ if(std::isspace(s[i])) ++result; } return result;}std::string buf("This is a pen.");count_space(buf); // 問題なし。3を返す
  39. 39. 実演
  40. 40. 使ってみよう"abcdefghijklmnopqrstuvwxyz"の空ではないすべての部分文字列を列挙する("a", "bcd", "defghijkl", ...)• std::stringだと ???byte• fundoshi::stringだと ???byte(インスタンス確保のための領域を除くと)
  41. 41. 使ってみよう"abcdefghijklmnopqrstuvwxyz"の空ではないすべての部分文字列を列挙する("a", "bcd", "defghijkl", ...)• std::stringだと 3276byte• fundoshi::stringだと 0byte(インスタンス確保のための領域を除くと)
  42. 42. 使ってみよう"abcdefghijklmnopqrstuvwxyz"の空ではないすべての部分文字列を列挙する("a", "bcd", "defghijkl", ...)• std::stringだと 3276byte n(n+1)(n+2)÷6 で計算可能• fundoshi::stringだと 0byte(インスタンス確保のための領域を除くと)
  43. 43. 使ってみよう"abcdefghijklmnopqrstuvwxyz"の空ではないすべての部分文字列を列挙する("a", "bcd", "defghijkl", ...)• std::stringだと 3276byte n(n+1)(n+2)÷6 で計算可能• fundoshi::stringだと 0byte(インスタンス確保のための領域を除くと)• 領域のサイズ:文字列長の3乗に比例• 文字列の個数:文字列長の2乗に比例
  44. 44. 応用例
  45. 45. 使い方(1/3)• もともとは、いろんな文字列アルゴリズムを 実装するのに欲しくなったので作った
  46. 46. 使い方(1/3)• もともとは、いろんな文字列アルゴリズムを 実装するのに欲しくなったので作った問題:文字列tから最速で部分文字列を検索するための(曖昧検索等は考慮しなくてよい)索引は?
  47. 47. 使い方(1/3)• もともとは、いろんな文字列アルゴリズムを 実装するのに欲しくなったので作った問題:文字列tから最速で部分文字列を検索するための(曖昧検索等は考慮しなくてよい)索引は?答え:tの部分文字列を全部格納しておく
  48. 48. 使い方(1/3)• もともとは、いろんな文字列アルゴリズムを 実装するのに欲しくなったので作ったSuffix Tree C ←"CACAO"の A O A Suffix Tree C C O (“CACAO”の O 部分文字列が A A 全部含まれてる) O O
  49. 49. 使い方(1/3)• もともとは、いろんな文字列アルゴリズムを 実装するのに欲しくなったので作ったSuffix Tree C ←"CACAO"の A O A Suffix Tree C C O (“CACAO”の O 部分文字列が A A メモリ量は 全部含まれてる) O テキスト長の2乗に比例 O →いくらなんでも多い
  50. 50. 使い方(1/3)• もともとは、いろんな文字列アルゴリズムを 実装するのに欲しくなったので作ったSuffix Tree C ←"CACAO"の A O A Suffix Tree C C O (“CACAO”の O 部分文字列が A A 全部含まれてる) O O
  51. 51. 使い方(1/3) • もともとは、いろんな文字列アルゴリズムを 実装するのに欲しくなったので作った fundoshi(4,1) Suffix Tree fundoshi(1,1) ←"CACAO"の fundoshi(0,2) Suffix Treefundoshi(4,1) (“CACAO”の 部分文字列が 全部含まれてる) fundoshi(4,1) fundoshi(2,3) fundoshi(2,3)
  52. 52. 使い方(1/3) • もともとは、いろんな文字列アルゴリズムを 実装するのに欲しくなったので作った fundoshi(4,1) Suffix Tree fundoshi(1,1) ←"CACAO"の fundoshi(0,2) Suffix Treefundoshi(4,1) (“CACAO”の 部分文字列が メモリ量は 全部含まれてる) テキスト長の比例で fundoshi(4,1) fundoshi(2,3) 済んじゃった! fundoshi(2,3)
  53. 53. 使い方(2/3) Webアプリケーションとかでも便利と思われる 例) 7文字を超えた部分を切り詰めて 「...」に置き換えたい 「本当にありがとうございました」→「本当にありがと...」//普通の方法if(buf.length() > 7) std::cout << buf.substr(0,7) << "..." << std::endl;else std::cout << buf << std::endl;
  54. 54. 使い方(2/3) Webアプリケーションとかでも便利と思われる 例) 7文字を超えた部分を切り詰めて 「...」に置き換えたい メモリがコピー 「本当にありがとうございました」→「本当にありがと...」//普通の方法 されちゃってるし!if(buf.length() > 7) std::cout << buf.substr(0,7) << "..." << std::endl;else std::cout << buf << std::endl;
  55. 55. 使い方(2/3) Webアプリケーションとかでも便利と思われる 例) 7文字を超えた部分を切り詰めて 「...」に置き換えたい 「本当にありがとうございました」→「本当にありがと...」//fundoshi.hppを使うとif(buf.length() > 7) std::cout << fundoshi::string(buf,0,7) << "..." << std::endl;else std::cout << buf << std::endl;
  56. 56. 使い方(3/3)Webアプリの裏方(フレームワークやWebサーバなど)でも便利と思われるSERVER_QUERY="hoge=11-17&piyo=NAME" ~~~~ ~~~~~ ~~~~ ~~~~ ↑欲しいのはこの4つ!※ C++のWebアプリフレームワークを 使ったことないのですが、このあたりの 事情をご存知の方いらっしゃったら 教えていただきたいです
  57. 57. 今後の展望
  58. 58. 今後の展望(1/2)必然的に部分文字列を取る関数はfundoshi::stringを返すと幸せになれるものが多いはず例) boost::match_results(正規表現などでの 検索結果の文字列を示すクラス)は std::stringを返すのだが、 fundoshi::stringで事足りることも多いよね→じゃあ自分で作ろうか
  59. 59. 今後の展望(2/2)ファイルストレージにも同様に適用できないか• 現状はRAM上の文字列にしか適用できない• 大容量のファイルを扱う場合などに便利• SSDなら読み込み時間の影響も少ない
  60. 60. おわりに
  61. 61. おわりに(1/2)fundoshi.hppのソースコードhttp://github.com/maraigue/fundoshi.hpp今回のデモ(部分文字列列挙)のソースコードhttp://github.com/maraigue/ clrhsapporocpp-20121117
  62. 62. おわりに(2/2)「部分文字列を取得するにあたって、メモリ余分に喰ってないか?」と思ったらfundoshi.hppのことを思い出して頂けると幸いです
  63. 63. ありがとうございました
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×