私は最近少し教えています。 学生の意識を広げるために、私は彼に次のタスクを尋ねました。
数値aとbの差を見つけるsub(a、b)関数を作成します。 ただし、関数のテキストに「-」文字を含めることはできません。好奇心の強い読者にとっては、記事を読むのを延期して、自分で問題を解決しようとする時が来ました。 したがって、彼が誤って以下の解決策のいずれかを見ないように、時計が12を打つまで溶けない雪片の写真を挿入します。
問題を定式化する際に、最近経験したトピックに関連する特定の方法を示唆しました。 しかし、その後、私は考えました:この言語には、非自明な可能性が豊富な方法はまだ存在しますか? この件に関する数時間の反省の結果、私はあなたと共有したいと思います。
一般的な考慮事項
減算を行わずに減算を行う最も簡単でグリッチの少ない方法は、何らかの方法で値「マイナス1」を取得し、次に記述することです。
return a + b * minusOne;
何らかの形で文字列「-」を取得した場合、単純にマイナス1に変更できます。
let minusOne = (minusChar + 1) | 0;
これらの小さなトリックなしでやりたい場合、痛みが待っています。 これは、まず特別な値(Infinity、NaN)によって配信され、第二に、数値に対するそれほど些細な操作では精度が失われる可能性があります。 しかし、これは私たちが試す必要がないという意味ではありません。 私たちを殺さないすべてが私たちを強くします。
最も明白な
私の意見では、初心者の頭に浮かぶ最初の方法は、 Array#indexOfを使用することです。 もちろん、これは体系的にフラナガンを順番に読んだ場合に最初に出会える適切なものではありません。 しかし、初心者はフラナガンを順番に読む必要はありません。なぜなら、彼はすぐに大量の不必要な情報に夢中になるからです。 配列#indexOfは単純さと実用性をうまく組み合わせているため、これを最も明白な解決策と考えています。
function sub(a, b){ let minusOne = [].indexOf(0); return a + b * minusOne; }
indexOfメソッドは、その名前が示すとおり、配列内の要素のインデックスを返します。 配列にそのような要素がない場合、特別な値-1が返されます。 とても便利です。
ビット演算
そして、これはいくつかの厳しいシシュニクに起こったはずだった最初のものです。 たとえば、次のように:
function sub(a, b){ let minusOne = ~0; return a + b * minusOne; }
javascriptのチルダは、ビットごとの否定を表します。 負数の内部表現の性質により、ゼロのビットごとの否定は魔法のようにマイナス1になります。 ちなみに、逆の場合も当てはまります。その理由は、次のように配列に入る要素の条件を記述する習慣がある人もいるからです。
if(~arr.indexOf(elem)){ //...
現在、 Array# includeの出現により、このハックの重要性は低下しています。
また、マイナス1はより洗練された方法で取得できます。 たとえば、ビット単位のシフト:
let minusOne = 1 << 31 >> 31;
数学
そして、これは数学が頭に浮かぶ最初のものです。 グローバルMathオブジェクトのメソッドには多くの方法があります。 例:
function sub(a, b){ let minusOne = Math.cos(Math.PI); return a + b * minusOne; }
または別の方法:
let minusOne = Math.log(1/Math.E); // minusOne = Math.sign(Number.NEGATIVE_INFINITY);
ところで、対数を使用したメソッドでは、最初にマイナス1を取得せずに、「直接」数値を減算できます。
function sub(a, b){ return Math.log( Math.E ** a / Math.E ** b); }
ただし、このアプローチの問題については、「一般的な考慮事項」ですでに書いています。
行
文字列「-」を取得する方法は多数あります。 おそらく最も明白なもの:
function sub(a, b){ let minusChar = String.fromCharCode(45); let minusOne = (minusChar + 1) | 0; return a + b * minusOne; }
let minusChar = "\u002d";
さらに、この文字は、既に含まれている文字列から取得できます。 たとえば、次のように:
let minusChar = 0.5.toExponential()[2]; // 0.5.toExponential() == "5e-1" minusChar = (new Date(0)).toISOString()[4]. //(new Date(0)).toISOString() == "1970-01-01T00:00:00.000Z"
ところで、マイナス記号を取得する場合、マイナス1を取得する必要はありません。 次のように実行できます。
function sub(a, b){ let minusChar = "\u002d"; return eval("(" + a + ")" + minusChar + "(" + b + ")"); }
もちろんこのためには、次の人生で白癬が生まれる必要がありますが、現在の文の前にこの記事を読んだ場合、明らかに失うものはありません。
年が若いとき
日付について話しているので、マイナス1を取得する別の方法を次に示します。
let minusOne = Date.UTC(1969, 11, 31, 23, 59, 59, 999);
事実は、JavaScriptの日付が「ボンネットの下に」いわゆる Unix時間 -1970年1月1日の午前0時からのミリ秒数。 したがって、1969年12月31日23:59:59および999ミリ秒で、この値は正確に-1でした。
自宅で繰り返さないでください
最後に、複雑で不十分な方法をいくつか紹介します。
両方の数値が正で有限であり、最初の数値が2番目の数値より大きい場合、除算を剰余で使用できます。
function sub(a, b){ let r = a % b; while(r + b < a){ r += b; } return r; }
これは、
a == a % b + b * n
、機能します。ここで、nは整数です。 したがって、
a - b == a % b + b * (n - 1)
、つまり、余りbに加算すると、遅かれ早かれ希望の値が得られます。
慎重に考えれば、サイクルを取り除くことができます。 実際、bが2回以上適合する場合にのみ、サイクルは0回以上の反復を通過します。 これは次のようにして回避できます。
function sub(a, b){ return (a + a) % (a + b); }
ただし、このメソッドは、負の数(演算子「%」が非常に奇妙に機能するという事実のため)、減算された値よりも大きい値、および特別な値では正しく動作しません。
そして最後に(ドラムロール、ファンファーレ)、計算数学の最高の伝統では、半除算法を使用して差を計算できます。
function sub(a, b){ var d = 1; // . , b , a var r = 0; // // d, . while(b + d < a){ d *= 2; } // r, while(b + r < a){ if(b + r + d > a){ d /= 2; }else{ r += d; } } // js - return r; }
繰り返しますが、このメソッドはa> = bであり、いずれの数値も無限またはNaNでない場合にのみ機能します。
これで終わりです。 記事に記載されている方法とは大幅に異なる方法を思いついた場合は、コメントに必ず記載してください。 良い金曜日を!