回答編集履歴
7
文章ちょっと修正
answer
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
結論
|
1
|
+
Chironianさんのコメントからの結論
|
2
2
|
|
3
3
|
```C++
|
4
4
|
#include <iostream>
|
6
結論だけ書いた
answer
CHANGED
@@ -1,3 +1,50 @@
|
|
1
|
+
結論
|
2
|
+
|
3
|
+
```C++
|
4
|
+
#include <iostream>
|
5
|
+
class ComplexInt
|
6
|
+
{
|
7
|
+
private:
|
8
|
+
int real;
|
9
|
+
int imag;
|
10
|
+
|
11
|
+
public:
|
12
|
+
ComplexInt(int real, int imag = 0) : real(real), imag(imag) {}
|
13
|
+
friend ComplexInt operator+(const ComplexInt &x, const ComplexInt &y)
|
14
|
+
{
|
15
|
+
return ComplexInt(x.real + y.real, x.imag + y.imag);
|
16
|
+
}
|
17
|
+
friend std::ostream &operator<<(std::ostream &os, const ComplexInt &ci)
|
18
|
+
{
|
19
|
+
os << ci.real;
|
20
|
+
if (ci.imag >= 0) {
|
21
|
+
os << "+";
|
22
|
+
}
|
23
|
+
os << ci.imag << "i";
|
24
|
+
return os;
|
25
|
+
}
|
26
|
+
};
|
27
|
+
|
28
|
+
|
29
|
+
int main()
|
30
|
+
{
|
31
|
+
const ComplexInt a(2, 3);
|
32
|
+
const ComplexInt b(1, -5);
|
33
|
+
std::cout << a << std::endl;
|
34
|
+
std::cout << b << std::endl;
|
35
|
+
std::cout << (a + b) << std::endl;
|
36
|
+
std::cout << (a + 1) << std::endl;
|
37
|
+
std::cout << (1 + a) << std::endl;
|
38
|
+
return 0;
|
39
|
+
}
|
40
|
+
```
|
41
|
+
|
42
|
+
何も問題なし、以上。
|
43
|
+
|
44
|
+
以下、駄文。
|
45
|
+
|
46
|
+
---
|
47
|
+
|
1
48
|
C++がだめな理由を考えました。
|
2
49
|
|
3
50
|
---
|
5
補足を追加
answer
CHANGED
@@ -239,8 +239,10 @@
|
|
239
239
|
|
240
240
|
ぶっちゃけ、C++とScalaでは、単にScalaは暗黙の型変換とか使えば、実装が少なくて済むので楽だよね、程度しか無いと思います。ただ、C++の方が細かく実装できる分、コストがー、コストがー、と言っている深淵を覗くことが大好きな人達には好まれるでしょう。
|
241
241
|
|
242
|
-
C++で何か嫌だと思う点の一つは、メンバー関数として実装できないことだと思います。オブジェクト指向原理主義者からみると、メソッドでない奴は駄目な奴だと勘違いしているのかも知れません。Scalaの暗黙の型変換もさほど変わらないと思うのですが、objectのメソッドだからいいとか言い出すのでしょうか…。
|
242
|
+
C++で何か嫌だと思う点の一つは、メンバー関数として実装できないことだと思います※。オブジェクト指向原理主義者からみると、メソッドでない奴は駄目な奴だと勘違いしているのかも知れません。Scalaの暗黙の型変換もさほど変わらないと思うのですが、objectのメソッドだからいいとか言い出すのでしょうか…。
|
243
243
|
|
244
|
+
※ friendsだとできるそうです。追記しています。
|
245
|
+
|
244
246
|
あと、たぶん、何か嫌だと思ってしまうのは、標準型に新たなメンバー関数を追加しているように見えると言うことではないでしょうか。じゃあ、ScalaやRubyはどうなのかというと、確かに、元々の標準型に何かを追加している訳ではありませんが、一歩離れれば、やっていることはさほどかわりありません。やり方が違うだけで目くじら立てるのはどうなのかとは思ってしまいます。
|
245
247
|
|
246
248
|
最後にHaskellを見てみましょう。Haskellの`a + b`は`(+)(a)(b)`です。他の関数と同じようにオーバーロードできます。オブジェクト指向なんかにして、無理にメソッドとかメンバー関数とか、そんなことをするより、全部関数にしておけば良かったのかも知れません。
|
4
崩れてた
answer
CHANGED
@@ -231,6 +231,7 @@
|
|
231
231
|
std::cout << (1 + a) << std::endl;
|
232
232
|
return 0;
|
233
233
|
}
|
234
|
+
```
|
234
235
|
|
235
236
|
では、これで一安心だねっていうかと思うと、ちょっと使っただけで、グローバルに動作を影響を与えています。あ、Scalaと暗黙の型変換を検索するところと検索の仕方だけは同じ所に戻っただけのようです。
|
236
237
|
|
3
C\+\+フレンズ
answer
CHANGED
@@ -242,4 +242,65 @@
|
|
242
242
|
|
243
243
|
あと、たぶん、何か嫌だと思ってしまうのは、標準型に新たなメンバー関数を追加しているように見えると言うことではないでしょうか。じゃあ、ScalaやRubyはどうなのかというと、確かに、元々の標準型に何かを追加している訳ではありませんが、一歩離れれば、やっていることはさほどかわりありません。やり方が違うだけで目くじら立てるのはどうなのかとは思ってしまいます。
|
244
244
|
|
245
|
-
最後にHaskellを見てみましょう。Haskellの`a + b`は`(+)(a)(b)`です。他の関数と同じようにオーバーロードできます。オブジェクト指向なんかにして、無理にメソッドとかメンバー関数とか、そんなことをするより、全部関数にしておけば良かったのかも知れません。
|
245
|
+
最後にHaskellを見てみましょう。Haskellの`a + b`は`(+)(a)(b)`です。他の関数と同じようにオーバーロードできます。オブジェクト指向なんかにして、無理にメソッドとかメンバー関数とか、そんなことをするより、全部関数にしておけば良かったのかも知れません。
|
246
|
+
|
247
|
+
---
|
248
|
+
|
249
|
+
君はC++が得意なフレンズなんだね。
|
250
|
+
|
251
|
+
```C++
|
252
|
+
#include <iostream>
|
253
|
+
class ComplexInt
|
254
|
+
{
|
255
|
+
private:
|
256
|
+
int real;
|
257
|
+
int imag;
|
258
|
+
|
259
|
+
public:
|
260
|
+
ComplexInt(int real, int imag) : real(real), imag(imag) {}
|
261
|
+
ComplexInt operator+(const ComplexInt &other) const
|
262
|
+
{
|
263
|
+
return ComplexInt(real + other.real, imag + other.imag);
|
264
|
+
}
|
265
|
+
ComplexInt operator+(int other) const
|
266
|
+
{
|
267
|
+
return ComplexInt(real + other, imag);
|
268
|
+
}
|
269
|
+
std::string toString() const
|
270
|
+
{
|
271
|
+
std::string str("");
|
272
|
+
str += std::to_string(real);
|
273
|
+
if (imag >= 0) {
|
274
|
+
str += "+";
|
275
|
+
}
|
276
|
+
str += std::to_string(imag) += "i";
|
277
|
+
return str;
|
278
|
+
}
|
279
|
+
int getReal() const { return real; }
|
280
|
+
int getImag() const { return imag; }
|
281
|
+
friend std::ostream &operator<<(std::ostream &os, const ComplexInt &ci)
|
282
|
+
{
|
283
|
+
os << ci.toString();
|
284
|
+
return os;
|
285
|
+
}
|
286
|
+
friend ComplexInt operator+(int x, const ComplexInt &y)
|
287
|
+
{
|
288
|
+
return ComplexInt(x, 0) + y;
|
289
|
+
}
|
290
|
+
};
|
291
|
+
|
292
|
+
|
293
|
+
int main()
|
294
|
+
{
|
295
|
+
const ComplexInt a(2, 3);
|
296
|
+
const ComplexInt b(1, -5);
|
297
|
+
std::cout << a << std::endl;
|
298
|
+
std::cout << b << std::endl;
|
299
|
+
std::cout << (a + b) << std::endl;
|
300
|
+
std::cout << (a + 1) << std::endl;
|
301
|
+
std::cout << (1 + a) << std::endl;
|
302
|
+
return 0;
|
303
|
+
}
|
304
|
+
```
|
305
|
+
|
306
|
+
メンバー関数にできたけど、これこれで、ありなんだそうです。C++書くのつかれた。
|
2
名前空間を追加
answer
CHANGED
@@ -61,8 +61,10 @@
|
|
61
61
|
|
62
62
|
Scalaは演算子がメソッドだ!と言っていますが、C++も演算子はメソッド相当であるメンバー関数として定義できます。その部分に違いはありません。しかし、メンバー関数として定義した場合は、`1 + a`のような整数が前に来るような足し算には対応できません。そのため、`ComplexInt operator+(int x, const ComplexInt &y)`というグローバル関数を別途定義する必要が出てきます。
|
63
63
|
|
64
|
-
グローバル空間を汚すことは**行儀が悪いコード**と言われます。しかし、C++の演算子オーバーロードではグローバル関数として定義せざるをえない場合があります。
|
64
|
+
グローバル空間を汚すことは**行儀が悪いコード**と言われます(つまり、Cだと関数一個も作れないと言うこと)。しかし、C++の演算子オーバーロードではグローバル関数として定義せざるをえない場合があります※。
|
65
65
|
|
66
|
+
※ 名前空間は汚す必要ない!って話なので、名前空間を汚さないの後から追記しています。
|
67
|
+
|
66
68
|
対して、Scalaでは左辺も含めた暗黙の型変換を定義できるため、次のようになります。
|
67
69
|
|
68
70
|
```Scala
|
@@ -117,7 +119,7 @@
|
|
117
119
|
}
|
118
120
|
```
|
119
121
|
|
120
|
-
ぱっと
|
122
|
+
ぱっと見、C++とは違ってグローバル関数を使ってないですので、グローバルを汚すと言うことはしていないように思えます。しかし、暗黙の型変換も暗黙のクラスもグローバルに影響を与えています。**どっちにしろグローバルを汚します。**ただ、足し算という処理は一つにまとめることができますので、C++よりはマシであると言えるのではないでしょうか。
|
121
123
|
|
122
124
|
おまけで、Rubyを見てみましょう。
|
123
125
|
|
@@ -168,4 +170,76 @@
|
|
168
170
|
|
169
171
|
RubyにはNumeric#coerceという仕組みがあります。これは、自分が知らない型なら相手のcoerceを呼び出して型をあわせようとする動作です。Numeric、つまり、数値をあらわす型のみですが、この仕組みにより、任意の数値のような型を追加することができます。実際にBigDecimal(任意精度十進数小数点数)やMatrix(行列)ではこの仕組みを使って、通常のInteger(整数)と足し算などができるようになっています。
|
170
172
|
|
171
|
-
この仕組みの利点は、グローバルを汚さないということです。他の型と動作が変わったわけではありません。しかし、欠点として、もともとの定義がcoerceを考慮していなければなりません。実質使えるのは数値のみです。
|
173
|
+
この仕組みの利点は、グローバルを汚さないということです。他の型と動作が変わったわけではありません。しかし、欠点として、もともとの定義がcoerceを考慮していなければなりません。実質使えるのは数値のみです。
|
174
|
+
|
175
|
+
---
|
176
|
+
|
177
|
+
名前空間を汚さなくても大丈夫だそうです。
|
178
|
+
|
179
|
+
```C++
|
180
|
+
#include <iostream>
|
181
|
+
namespace ci {
|
182
|
+
class ComplexInt
|
183
|
+
{
|
184
|
+
private:
|
185
|
+
int real;
|
186
|
+
int imag;
|
187
|
+
|
188
|
+
public:
|
189
|
+
ComplexInt(int real, int imag) : real(real), imag(imag) {}
|
190
|
+
ComplexInt operator+(const ComplexInt &other) const
|
191
|
+
{
|
192
|
+
return ComplexInt(real + other.real, imag + other.imag);
|
193
|
+
}
|
194
|
+
ComplexInt operator+(int other) const
|
195
|
+
{
|
196
|
+
return ComplexInt(real + other, imag);
|
197
|
+
}
|
198
|
+
std::string toString() const
|
199
|
+
{
|
200
|
+
std::string str("");
|
201
|
+
str += std::to_string(real);
|
202
|
+
if (imag >= 0) {
|
203
|
+
str += "+";
|
204
|
+
}
|
205
|
+
str += std::to_string(imag) += "i";
|
206
|
+
return str;
|
207
|
+
}
|
208
|
+
int getReal() const { return real; }
|
209
|
+
int getImag() const { return imag; }
|
210
|
+
};
|
211
|
+
|
212
|
+
std::ostream &operator<<(std::ostream &os, const ComplexInt &ci)
|
213
|
+
{
|
214
|
+
os << ci.toString();
|
215
|
+
return os;
|
216
|
+
}
|
217
|
+
ComplexInt operator+(int x, const ComplexInt &y)
|
218
|
+
{
|
219
|
+
return ComplexInt(x, 0) + y;
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
int main()
|
224
|
+
{
|
225
|
+
const ci::ComplexInt a(2, 3);
|
226
|
+
const ci::ComplexInt b(1, -5);
|
227
|
+
std::cout << a << std::endl;
|
228
|
+
std::cout << b << std::endl;
|
229
|
+
std::cout << (a + b) << std::endl;
|
230
|
+
std::cout << (a + 1) << std::endl;
|
231
|
+
std::cout << (1 + a) << std::endl;
|
232
|
+
return 0;
|
233
|
+
}
|
234
|
+
|
235
|
+
では、これで一安心だねっていうかと思うと、ちょっと使っただけで、グローバルに動作を影響を与えています。あ、Scalaと暗黙の型変換を検索するところと検索の仕方だけは同じ所に戻っただけのようです。
|
236
|
+
|
237
|
+
---
|
238
|
+
|
239
|
+
ぶっちゃけ、C++とScalaでは、単にScalaは暗黙の型変換とか使えば、実装が少なくて済むので楽だよね、程度しか無いと思います。ただ、C++の方が細かく実装できる分、コストがー、コストがー、と言っている深淵を覗くことが大好きな人達には好まれるでしょう。
|
240
|
+
|
241
|
+
C++で何か嫌だと思う点の一つは、メンバー関数として実装できないことだと思います。オブジェクト指向原理主義者からみると、メソッドでない奴は駄目な奴だと勘違いしているのかも知れません。Scalaの暗黙の型変換もさほど変わらないと思うのですが、objectのメソッドだからいいとか言い出すのでしょうか…。
|
242
|
+
|
243
|
+
あと、たぶん、何か嫌だと思ってしまうのは、標準型に新たなメンバー関数を追加しているように見えると言うことではないでしょうか。じゃあ、ScalaやRubyはどうなのかというと、確かに、元々の標準型に何かを追加している訳ではありませんが、一歩離れれば、やっていることはさほどかわりありません。やり方が違うだけで目くじら立てるのはどうなのかとは思ってしまいます。
|
244
|
+
|
245
|
+
最後にHaskellを見てみましょう。Haskellの`a + b`は`(+)(a)(b)`です。他の関数と同じようにオーバーロードできます。オブジェクト指向なんかにして、無理にメソッドとかメンバー関数とか、そんなことをするより、全部関数にしておけば良かったのかも知れません。
|
1
左辺!左辺!左辺!
answer
CHANGED
@@ -63,7 +63,7 @@
|
|
63
63
|
|
64
64
|
グローバル空間を汚すことは**行儀が悪いコード**と言われます。しかし、C++の演算子オーバーロードではグローバル関数として定義せざるをえない場合があります。
|
65
65
|
|
66
|
-
対して、Scalaでは暗黙の型変換を定義できるため、次のようになります。
|
66
|
+
対して、Scalaでは左辺も含めた暗黙の型変換を定義できるため、次のようになります。
|
67
67
|
|
68
68
|
```Scala
|
69
69
|
class ComplexInt(val real: Int, val imag: Int) {
|