Skip to content

Conversation

}

private:
string canonicalize_email(string& email) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

& 関係の質問ですが、意図として、

for (auto email : emails)

ここはコピーして、

string canonicalize_email(string& email)

コピーせずに参照で受け取って、

email.replace(0, at_pos, local_part); 

ここで引数を破壊的に変更し、さらにそれをコピーして返す、でいいですか。

  • 速くするためにコピーを減らしたい
  • numUniqueEmails を呼んだ人が驚かないように壊したくない

は理解できる欲求ですが canonicalize_email の仕様の整合が取れていないように思います。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この書き方でコピーが返されるという点を考慮できていなかったです。

string& canonicalize_email(string& email)

とすべきでした。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

はい。それは合理的な選択肢です。

もう一つ、

string canonicalize_email(const string& email)

入力を破壊せずに、変更したものをコピーで返す、というのも選択だと思います。

ここでしたいことは「email から正規化した email を作る」ということですが、結局、使い方を見ると、「破壊していいようなコピーを作る」 + 「破壊的に変更する」という形になっているので、まとめた関数を作るというのも一つです。

他、Google Style Guide の方法だと、「入力を破壊して変更する」際には

void canonicalize_email(string* email);

という signature にする約束になっています。これだと呼び出し元が

canonicalize_email(&email);

&がつくので、破壊していることが見えるからです。
しかし、これはとてもローカルなルールなので、従う必要はありません。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

昔は、string のようなクラスを直接返すと、コピーコンストラクタが走り最適化がされるとも限らなかったのですが、C++11 以降は move も整備されているので、気にしなくてはいけない度合いは下がっているかもしれませんね。
set::insert

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

他、Google Style Guide の方法だと、「入力を破壊して変更する」際には

補足すると、

  • 非 optional の出力パラメーター、および入出力パラメーターは参照で渡す。
  • optional の出力パラメーター、および入出力パラメーターは非 const ポインターで渡す。

ようです。

https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs

Non-optional input parameters should usually be values or const references, while non-optional output and input/output parameters should usually be references (which cannot be null). Generally, use std::optional to represent optional by-value inputs, and use a const pointer when the non-optional form would have used a reference. Use non-const pointers to represent optional outputs and optional input/output parameters.

string canonicalize_email(string& email) {
string canonicalized_email = "";
auto at_pos = email.rfind('@');
string local_part = email.substr(0, at_pos);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私は、domain 部分も文字列として変数を作ってしまって、replace ではなくて

return canonicalized_email + domain;

としたほうが読みやすいと思います。
要するに名前がつくものにはつけてしまえということです。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど、操作しない箇所には触れないほうが良いと考えていましたが、名前をつけるとわかりやすいですね。

Time Order: O(n)
Space Order: O(n)

方針は早くに定まっていたものの、C++の文字列操作が思うようにいかずに時間がかかってしまった。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

まあ、これはそういうものであり慣れですね。定期的にリファレンスを読みましょう。

auto at_pos = email.find_first_of("@");
string local_part = email.substr(0, at_pos);
auto plus_pos = local_part.find_first_of("+");
if (plus_pos == std::string::npos) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string と std::string のうちどちらかに統一したほうが、読むにあたっての認知負荷が下がると思います。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます、nposを使い慣れていなかったのでstd::から書いてしまっていました。

}

private:
string canonicalize_email(string email) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

関数名は UpperSnake とすることをおすすめいたします。

https://google.github.io/styleguide/cppguide.html#Function_Names

Ordinarily, functions should start with a capital letter and have a capital letter for each new word.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
snake caseとcamel caseが混在しているのが良くないですね
leetcodeだとデフォルトで用意されている関数名がcamel caseなので、そちらに合わせるようにします。

}

private:
string canonicalize_email(string email) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

引数の型のサイズがレジスター幅に比べて十分に大きい場合は、過多なメモリーコピーを避けるため、 const 左辺値参照で渡すことをおすすめいたします。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます、メモリコピーの観点を持てていませんでした。

}

private:
string canonicalize_email(string email) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

戻り値の型を string にした場合、 RVO (Return Value Optimization) が行われない場合、ヒープメモリーのコピーが発生します。これはプログラムの速度低下を招きます。これについては意識しましたか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RVOについては初耳でした、コメントありがとうございます。
戻り値の型を気にするようにします。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

void canonicalize_email(string& input, string& output)

のようにして、引数のoutputには空の文字列を渡し、outputを書き換える操作をすることで、stringのコピーを明示的に抑えるような方法もあるようです。


private:
string canonicalize_email(string email) {
const auto at_pos = email.rfind("@");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const を付けている変数と const を付けていない変数がありますが、それらの違いは何でしょうか?意図が分かりませんでした。特に意図がなければ、ローカル変数には const を付けないほうがシンプルに感じられます。意図がある場合は明示的に付けたほうがよいと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントありがとうございます。
変更されないものについてはconstをつけ、変更されないものはつけない、と使い分けていました。
constを付与すべき意図が明確な時に限って利用するようにします。


private:
string canonicalize_email(string& email) {
string canonicalized_email = "";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string のデフォルトコンストラクターで、空文字に初期化されるため、 = "" は消したほうがよいと思います。

}

private:
string canonicalize_email(string& email) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

他、Google Style Guide の方法だと、「入力を破壊して変更する」際には

補足すると、

  • 非 optional の出力パラメーター、および入出力パラメーターは参照で渡す。
  • optional の出力パラメーター、および入出力パラメーターは非 const ポインターで渡す。

ようです。

https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs

Non-optional input parameters should usually be values or const references, while non-optional output and input/output parameters should usually be references (which cannot be null). Generally, use std::optional to represent optional by-value inputs, and use a const pointer when the non-optional form would have used a reference. Use non-const pointers to represent optional outputs and optional input/output parameters.

string canonicalize_email(string& email) {
auto at_pos = email.rfind('@');
string local_part = email.substr(0, at_pos);
std::regex dot_and_plus_suffix("\\.|\\+.*");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

正規表現は読むにあたり認知負荷が高く感じます。正規表現の意図をコメントで書くことをおすすめいたします。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど確かに、ありがとうございます、今後正規表現を使用するときは意図を追記します。

@colorbox colorbox merged commit d4ed7ed into main Feb 1, 2025
@colorbox colorbox deleted the 929 branch February 1, 2025 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants