🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
if

if文とは様々なプログラミング言語で使用される制御構文の一種であり、条件によって処理の流れを制御します。

スコープ

スコープとは、プログラム内で変数名など、参照可能な有効範囲のことを指します。

キャスト

キャストとは、オブジェクトの型の変換が許可された場合に、明白に別の型への変換を行うプロセスのことです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

899閲覧

型変換の回数を減らしたいが、スコープは小さくしたい。

_kari_

総合スコア34

if

if文とは様々なプログラミング言語で使用される制御構文の一種であり、条件によって処理の流れを制御します。

スコープ

スコープとは、プログラム内で変数名など、参照可能な有効範囲のことを指します。

キャスト

キャストとは、オブジェクトの型の変換が許可された場合に、明白に別の型への変換を行うプロセスのことです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2021/02/19 07:49

前提

タイトルが曖昧でごめんなさい。
ちょっと日本語での説明が思いつかなかったのでJavaScriptのコードで説明させてください。

JavaScript

1//引数argはSymbolもしくは任意の文字列 2function myFunc(arg) { 3 if (typeof arg === "symbol") { 4 //Symbolを使った処理 5 } else if (Number.isInteger(+arg)) { 6 let i = +arg; 7 //引数の文字列を整数に変換した変数iを使った処理 8 } else { 9 //引数が整数に変換できない場合、文字列として処理 10 } 11}

上のコードの真ん中のelse ifブロックで、条件判定のときに単項プラス演算子で引数を数値型に変換しているのですが、ブロック内の処理でも引数を整数にしたものが必要になるので再度単項プラス演算子で変換をしています。
ここで、同じ変換を二回もしているのが無駄だなあと感じました。

そうかといって、

JavaScript

1function myFunc(arg) { 2 let i = +arg; 3 if (typeof arg === "symbol") { 4//(後略)

のように冒頭で変換しておくようにすると、引数がSymbolだった場合は変換できずエラーになります。

Symbolを避けて、

JavaScript

1function myFunc(arg) { 2 if (typeof arg === "symbol") { 3 //Symbolを使った処理 4 } else { 5 let i = +arg; 6 if (Number.isInteger(i)) { 7 //引数を整数に変換した変数iを使った処理 8 } else { 9 //引数が整数に変換できない場合、文字列として処理 10 } 11 } 12}

のようにするとエラーも変換の二度手間も省けるのですが、
「内側のelseではiは使わないから、iのスコープは内側のifだけでいいんだけどなあ」と細かいことが気になってしまいました。

実現したいこと

こういった場合に、

  • 型変換は一回だけ行う。
  • 変換した変数のスコープは、実際に使うブロック(一番上のコードでは真ん中のelse ifブロック)に抑える。

という条件を満たしたコードは書けるでしょうか。

こうすれば書けるよ!という方法があれば教えていただきたいです。
また、そんなコードは書けないということならその旨を教えていただけると嬉しいです。

そもそも関数myFuncの設計自体が無茶という点には目をつぶっていただけると幸いです。できないならできないということで理解しますので…

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

「内側のelseではiは使わないから、iのスコープは内側のifだけでいいんだけどなあ」

ifのカッコ内でも使っていますので、この感じ方がおかしいだけかと思います。

投稿2021/02/19 08:20

maisumakun

総合スコア145963

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

_kari_

2021/02/19 08:32

ifのカッコ内は外側のスコープになるんですね。 forのカッコ内がブロックスコープになるのでその認識に引っ張られていました。
maisumakun

2021/02/19 08:37

> forのカッコ内がブロックスコープになるのでその認識に引っ張られていました。 forのカッコ内は、内側とも外側とも違う独自のスコープです。
_kari_

2021/02/19 08:41

そうなんですか? 内側と同じスコープだと思っていました。 その点について勉強したいのですがどのようなキーワードで調べればよいでしょうか。
_kari_

2021/02/19 09:18

ご提示いただいたリンクの「等価なコード」を読んで腑に落ちました。 ありがとうございます。 偶然ですが、maisumakunさんの過去回答も見つけ、とても参考になりました。 https://teratail.com/questions/310641
guest

0

ベストアンサー

変数 i

Number.isInteger() の実行前に「型変換後の値」を変数 i に格納して下さい。

JavaScript

1function myFunc (arg) { 2 let i; 3 4 if (i = +arg, Number.isInteger(i)) { 5 //引数の文字列を整数に変換した変数iを使った処理 6 } 7}

早期リターン

JavaScript

1function myFunc(arg) { 2 if (typeof arg === "symbol") { 3 return /* Symbolを使った処理 */ ; 4 } 5 6 let i = +arg; 7 8 if (Number.isInteger(i)) { 9 return /*引数を整数に変換した変数iを使った処理 */ ; 10 } 11 12 let string = String(arg); 13 14 // 引数を文字列として処理 15}

Re: kari さん

投稿2021/02/19 08:02

編集2021/02/19 08:40
think49

総合スコア18189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

_kari_

2021/02/19 08:29

そのコードだとスコープは質問文の最後のコードと同じになりませんか? (let i = +arg, Number.isInteger(i))とできたら理想なのですが、「let i = +arg」は式ではないのでこうは書けない…
think49

2021/02/19 08:31

変数 i のスコープを最小限にするなら、_kari_ さんが書いた下記コードを使う事になります。 else { let i = +arg;
think49

2021/02/19 08:33

> そのコードだとスコープは質問文の最後のコードと同じになりませんか? 最後のコードよりも私の回答コードはスコープが広いといえるでしょう。 ブロックのネスト構造を敬遠しているようでしたので、あえてこのように書きましたが、最小限のスコープにプライオリティをおくなら、_kari_ さんが書いたコードが正解だと思います。
_kari_

2021/02/19 08:37

すみません、ifのカッコ内がブロックスコープになると思い込んでいました…(for文のように) 私のスコープについての認識がおかしかったのが問題の根幹でした。
think49

2021/02/19 08:41

個人的には、早期リターンで書きます。 回答に追記しました。
_kari_

2021/02/19 08:50

早期リターンって個人ブログの古めの記事でよく見かける印象があって(偏見)少し敬遠していたのですが、こう見るとすっきり見通せていいですね。 もう少し勉強してみます。
think49

2021/02/19 09:00 編集

おそらく、_kari_さんは構造化プログラミング的な手法を好んで使っていますが、階層を浅くする事を優先するなら、早期リターンは一つの解決法になります。 私は「構造化プログラミング」も一つの完成系だと思うので、階層が深くなったとしても採用する価値はあると思っていますが…。 結局はトレードオフなので、状況別に使い分けるしかないですね。
_kari_

2021/02/19 09:29

恥ずかしながら自分用のツールを作るくらいしかプログラミングの機会がなく、「構造化プログラミング」も名前だけは聞いたことがあるけど意味は知らないくらいでした。調べてみて、なるほどとなりました。 階層が深くなって書いた自分でも何がなんだかわからなくなることもあるので、今後必要な時には使い分けるようにします。
think49

2021/02/19 09:59 編集

構造化プログラミングのポイントは「一つの入口(仮引数)」と「一つの出口(return)」を持つことで、対する早期リターンは「一つの入口」と「複数の出口」を持ちます。 構造化プログラミングは出口が一つなので、後ろから遡ってコードを確認できますが、早期リターンは出口が複数なので、最初から最後まで全てに目を通さなければ全体把握できません。 参考までに、ES2020 では Number.isInteger で早期リターンのアルゴリズムを採用しています。 https://262.ecma-international.org/11.0/#sec-number.isinteger 現在は早期リターンが好まれる傾向にありますが、一長一短なので両方試して判断するのがベターだと私は思います。
_kari_

2021/02/19 10:16

古いどころかES2020でも採用されている方式なんですね。 おかげさまでだいぶ視野が広がりました。ありがとうございます。 一応、 { let i = +arg; if (Number.isInteger(i)) { return /*引数を整数に変換した変数iを使った処理 */ ; } } のように早期リターンにしてからブロックで包めば私が当初やりたかったことは実現できました。いい方法とは思わないけど… ひとまず解決済ということにしたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問