プロが教える店舗&オフィスのセキュリティ対策術

<データ型>1. 以下のようなケースでxに5という値を代入すると1行目の「let x = 1;」で「x」の値が5に変わるというのは理解できます。
この場合、2行目は「y」の値は「1」のままです。「5」でないのがわかりません。
代入したことによって「let y =x」へは影響を及ぼさないのはなぜですか?
少なくともスコープの範囲以内は代入の影響が出るものと考えてしまいます。


{

let x = 1;
let y = x;

x = 5;

console.log(x); //5
console.log(y); //1

}


<配列>
2.このような配列の場合、「x」に配列である[1, 2];が代入されているわけですよね。
以下のように「x」に5を代入した場合、x = 5 とはならないものなのでしょうか?
console.logで見てみると、[5, 2]となっています。

また、2行目も[5, 2]となっています。
上の<データ型>のケースをもとに考えるならば、ここは[1, 2]のままでないのはなぜなのでしょうか?

{

let x = [1, 2];
let y = x;

x = 5;

console.log(x); //[5, 2]
console.log(y); //[5, 2]


}

初心者です。上記2点はJavaScriptの基礎の辺りで説明されている事柄です。
どうも釈然としません。

どなたか分かりやすく教えていただけないでしょうか。よろしくお願いいたします。

質問者からの補足コメント

  • うーん・・・

    すみません。配列の方は「x = 5」ではなく、「x[0]=5」でした。

    「x」の0番目ということですので、「let x =[5, 2]」というのは分かります。

    1つ目で「5」を代入したときのように「y」の値は変わらなかったのに、
    2つ目の配列の方では、[5, 2]と「5」が代入されています。

    この違いを教えてください。 失礼しました。よろしくお願いいたします。。

      補足日時:2020/08/21 15:50

A 回答 (3件)

すごいざっくりと書きますので、正確なところは後からきっちり勉強してください。



Javascriptの場合。
・ = は、数学で使われている「等しい」とは違います。
y=x ;

数学なら「常にy=xであり、xが変化すればyも変化する」
となりますが、Javascirptでは
・この命令を実行した時点でのxの値を求めて、その値をyに代入する。
後からxを変えたところで、yは関係ない
となります。

また。
・変数は「対象物を指差す矢印」と考えるとよいでしょう。
・変数から読む、ということは、変数の指差す先を意味します。

x= 1 ;

x→「1」
という矢印です。
y=x ;

y→x
ではありません。xから読むので、その指差す先である
y→「1」
ということになります。

ここで
x=5;
とすると、 xの矢印が変更になって
x→「5」   xから「1」への矢印は外れる
になります。
よって、y→「1」のままです。



let x = [1, 2];
let y = x;
の場合、
「配列α」 : [1,2]
とすると

まず、配列は「矢印の集り」と考えられます。
つまり
「配列α」
 [0]→「1」
 [1]→「2」
です。
また
x→「配列α」
です。

y=x は、xが指差す先である
y→「配列α」
となります。
つまり
x

「配列α」:[0]→「1」 , [1]→「2」

y
という状態になります。

x[0]=5
というのは、xを通して「配列α」にアクセスし、その[0]の矢印を「5」へ変える、ということになります。
つまり
x

「配列α」:[0]→「5] (「1」への矢印は外れる) , [1]→「2」

y

この状態で
console.log(x);
console.log(y);
とすれば
console.log(「配列α」);
console.log(「配列α」);
ということなので、同じものが出力されることになります。



この考え方は、現在主流のプログラミング言語の多くで採用されているので、覚えておきましょう。


余談かもしれませんが
・C言語,C++のように、この矢印を扱うための変数(ポインタ)と、実体をそのまま扱う変数とに分かれているものもあります。
・関数型言語と呼ばれる種類のものでは、 y=x が数学的な「yとxは常に等しい」と扱い、xが変化するとyも変化する、というものもあります。
    • good
    • 0
この回答へのお礼

ご丁寧にありがとうございました。
今回の件は理解出来ました。
またよろしくお願いいたします。

お礼日時:2020/08/23 12:07

あー、うん、なるほど。



まず一つ目ですよね。

➜ ~ rhino
Rhino 1.7.7.1
js> let x = 1;
js> let y = x;
js> x = 5;
5
js> print(x);
5
js> print(y);
1
js>

> この場合、2行目は「y」の値は「1」のままです。「5」でないのがわかりません。
> 代入したことによって「let y =x」へは影響を及ぼさないのはなぜですか?

この疑問自体は面白いです。で、仮にC言語での「悪名高い」ポインタ使った操作だったとしたら、貴方の感覚通り、の現象を見るでしょう。ある意味「貴方の勘は正しい」。
逆の言い方すると、上の例だと「ポインタを使った操作ではないから」ってのが答えです(笑)。答えになってないようですが、取り敢えずはそう言っておきます。

xに1と言う値が入っています。良くある「初心者向けのプログラミングの説明」風に言うと、「xと名付けられた箱には1と言う整数値が収まってる」と。yにxを代入する、と言うのは別にyの指し示す先が「xと名付けられた箱」になってるわけではないです(これがC言語だと「ポインタyにxと言う箱のアドレスを代入する」と言う事をやって、強制的に「マジで」xとyを関連させる、なんて事が出来ますが)。実際は「xと名付けられた箱に入ってる1と言う整数値をyと名付けられた箱にコピーする」のを代入として表現しているだけ、です。従って、yと言う箱には既に1がコピー済みで、この時点で、xとyとの間に直接的な「関連」はありません。だからxの中身がどう変わろうと「コピー済みの」yの値には何も変化がないのです。

繰り返し言っておきますが、貴方の着眼点は面白い。だから全く「間違ってる」わけではないです。ある種のプログラミング言語にある「ポインタ」と言う機能は貴方の勘通りの動作を実現させます。ただ、JavaScriptやフツーのプログラミング言語は、単純に「そういう設計をしていない」だけですね。この辺はぶっちゃけ、「設計者が考えた通りに動く」のがプログラミング言語なので、貴方の着眼点が「面白い」とか「正しく」あっても、設計者がそう考えてなければそう動かないよ、ってだけの話ですね。

ちなみに、JavaScriptのモデルになったSchemeって言語でも、同様の動きをします。

➜ ~ csi
CHICKEN
(c) 2008-2019, The CHICKEN Team
(c) 2000-2007, Felix L. Winkelmann
Version 5.1.0 (rev 8e62f718)
linux-unix-gnu-x86-64 [ 64bit dload ptables ]

#;1> (define x 1)
#;2> (define y x)
#;3> (set! x 5)
#;4> x
5
#;5> y
1
#;6>

> 配列

さて、要するに単純な数値データを使った場合の変数と、配列を使った場合とじゃ挙動が違うじゃねぇか、ってのがトピックですよね(笑)。何でやねん、と(笑)。
いや、これも「何でもクソもない」んですよ(笑)。要するに「そういう風に設計してるから」なんです。全ては設計者の思うがまま(笑)。

とは言っても一応理由はあるんです。要するに「配列ってのはフツーの整数や浮動小数点数に比べるとデータがデカいから」なんです。データがデカいと同じデータを(このケースでは)二個まずは用意出来ないといけない。でもそうすると明らかにメモリをたくさん使用する事になっていまう。まあ、今のメモりの基準だと別にそこまでデカくはないんだけど、一方JavaScriptは一応「ブラウザで動く」前提で設計されてて、ブラウザに割り当てられてるメモリの中からJavaScriptにまたメモリを割り当てる、とかそういうカンジになってんじゃねぇのかな。だから「たとえPCにメモリが潤沢にあってもJavaScriptがそれをガーッと自由に使えるわけではない」。JavaScriptは要するに「下請け」ですからね(笑)。下請けはそこまで自由はないのです。

とJSの立場で書きましたが、実はこういう風に「メモリ効率上、配列やリストは"明示的に"コピーしろ」って命令が無い限り、変数同士で「共有」するような設計をしてる言語は結構多いです。「自動的に複製を作る」って言語ってあんまねぇんじゃねぇかな。
JSの元ネタ、Schemeでも、

#;6> (define x '(1 2))
#;7> (define y x)
#;8> (set-car! x 5)
#;9> x
(5 2)
#;10> y
(5 2)
#;11>

と言うように変数xとyでリスト(配列)を共有しています。
一方、明示的に「コピーせよ」と指令する事も可能で、

#;1> (import srfi-1) ;; ユーティリティを使う
; loading /var/lib//chicken/11/srfi-1.import.so ...

Note: re-importing already imported identifier: assoc

Note: re-importing already imported identifier: member
; loading /var/lib//chicken/11/srfi-1.so ...
#;2> (define x '(1 2))
#;3> (define y (list-copy x)) ;; ここで明示的にコピーせよ、と命令
#;4> (set-car! x 5)
#;5> x
(5 2)
#;6> y
(1 2)
#;7>

とすると、「貴方の期待したような」結果になりますね。
また、昨今流行りのPythonだと、

Python 3.8.2 (default, Jul 16 2020, 14:00:26)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license()" for more information.
>>> x = [1, 2]
>>> y = x
>>> x[0] = 5
>>> x
[5, 2]
>>> y
[5, 2]
>>>

Rubyだと、

➜ ~ irb
irb(main):001:0> x = [1, 2]
irb(main):002:0> y = x
irb(main):003:0> x[0] = 5
irb(main):004:0> x
=> [5, 2]
irb(main):005:0> y
=> [5, 2]
irb(main):006:0>

と、どちらもやっぱりJSと同じような結果になります。

将来的に、もっとメモリが潤沢な時代になれば、変数に「どんな種類のものを代入しても」一貫した結果になるかもしれません。
ただ、現時点だと、単純なデータ型と配列(やその類)だと、貴方が今経験して見た通り、「違う挙動になる」事が多いです。それだけ、メモリの使い方、と言うのに言語設計者は神経質になっているのです。
    • good
    • 0
この回答へのお礼

値渡し、参照渡し等のキーワードで合致しました。
配列やオブジェクトはデータ量が多いので値を参照するだけという説明がありました。
やはりメモリの都合のようです。
専門的、詳細な説明ありがとうございました。とても勉強になりました。

お礼日時:2020/08/23 12:11

1.手続き言語だから当たり前。


記述した順番の手続き処理をやるから、何もしなければ何も変わらない。

y = x;はxとyを常に等しくしなさい、と言う意味じゃない。←←全言語共通。
変数yに変数xの内容を代入する、と言う意味。

let x = 1; 実行されるとxには1が入る
let y = x; 実行されるとyにはxの今の値が入る、つまり1が入る
x = 5; 実行されるとxには5が入る
yは何もして無いから何も変わらず1のまま。



2.記述が不正確です、本当にそう記述しましたか?
実際に記述した通りにここへ書いて。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
記述は以下の通りです。
値渡し、参照渡しと言って配列やオブジェクトはデータ量が多いので
こういうやり方だということでした。

またよろしくお願いいたします。

{
let x = 1;
let y = x;
x = 5;
console.log(x); //5
console.log(y); //1
}
{
let x = [1, 2];
let y = x;
x[0] = 5;
console.log(x); //[5, 2]
console.log(y); //[5, 2]

}

お礼日時:2020/08/23 12:24

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!