FP对赋值运算符重载的实现有些不合理
pascal吧
全部回复
仅看楼主
level 1
黄金止手 楼主
operator := (x:TypeA) y:TypeB;
这里整个子程序被实现成 function 而非 procedure,y是函数返回值,而非赋值号左边变量的引用。
假设 TypeB 变量的大小很大或赋值次数很多,那么这种实现会造成不小的效率下降,效率正常的形式是
procedure SetFrom(var y:TypeB; const x:TypeA);
同样 inc 和 dec 运算符亦有此问题。
FP 甚至有这两者的返回值与首个参数类型不相同也能编译的 bug 。准确来说,假如使用了这个运算符,且返回值不能赋值给首个参数,才会编译错误。
简而言之, OI 里拿 Pascal 的重载运算符写高精度会有一定的效率牺牲。
但用 C++ 的话问题不大,甚至可以认为对效率没有影响。
Pascal (FP?) 的这种运算符重载更像是为引用类型打造的,使用于值类型(当 size 很大)时需要谨慎。
2016年09月04日 12点09分 1
level 1
黄金止手 楼主
不过,对于返回 Boolean 的关系运算符,对效率造成的影响相对较小。因为可以用 const 避免大量内存复制。
若出现高精度数的比较,则重载比较运算符是不错的选择。
在记录或数组类型的排序中,为了使代码更清晰,也可以选择重载比较运算符。
2016年09月04日 12点09分 2
level 9
Var参显然是不行的啊。如果参数是字面量的话就直接崩了。所以必须是function形式的。
返回值类型和首个参数类型不相同显然不是bug啊。。2/3返回实数从数学上显然是合理的,真正奇葩的是C语言啊。。
2016年09月06日 20点09分 3
赋值号左边的东西(左值)必须是可以传入 var 的。字面常量表本来就是不能赋值的。 而 inc 返回值类型和首个参数不符的意思是对 x 进行 inc(x, y) 后,x 的类型可以不一样,但还要赋值回 原来的 x ,这显然很不合常理。 (我说的好像不是 + 和 / 吧)
2016年09月07日 04点09分
刚才说错了,赋值号左边确实有不能取地址的东西。
2016年09月07日 09点09分
@黄金止手 inc本来就不是操作符啊,至少从数学上说并没有这样的函数,inc纯粹是过程式语言东西。
2016年09月07日 10点09分
@黄金止手 从数学上讲,无论是+还是:=,操作符应保持形式的一致。如果第一个是var参,那么b:=1+a应该是什么结果?
2016年09月07日 10点09分
level 1
黄金止手 楼主
被抽了……我想明白了。
属性这东西能在赋值号左边却不是左值,不能取引用。
[喷]因为支持属性,所以不能按引用赋值。
2016年09月07日 09点09分 6
语言和实现是两回事。这里的问题是var参本来就是不合理的。如果有必要,即使是const参编译器也可以当作var参处理的。
2016年09月07日 10点09分
@ax_pokl 不,这还是语言内容。属性本身不能传入var参数,但能在赋值号左边,这是规定。但对于变量,operator (x:TypeA) y:TypeB 的语义就成了 p:=expr 时的赋值语句中,TypeB 类型变量除了必须的 p ,还有一个临时的 y 。
2016年09月07日 11点09分
@ax_pokl 对变量赋值时结果类型的变量需要出现两个,这显得累赘。但对属性赋值时这么做是正常的,因为属性不是变量,结果类型的变量只需要存在一个。
2016年09月07日 11点09分
@黄金止手 这又是哪门子的规定-_-。属性本身就不是pascal的东西,在pascal中属性可以被看作是结构体(记录)的成员变量。既然是变量那自然是可以被var的。
2016年09月07日 14点09分
level 14
。。感觉没有问题啊(先把const和var搞明白再发表言论吧。。)
难道编Pascal语言的人傻么,,就没有优化的啊~
你自己试试效率到底降低没再来说吧。。。。
2016年09月08日 04点09分 11
[喷]我试过了。通过输出了变量地址查看的。FP的IDE还能直接看汇编调试呢。你真当我什么都没看过就大放阙词?
2016年09月08日 04点09分
@黄金止手 。。。那所谓的效率低是你自己没有写好吧,其效率是不会有常数倍低于函数过程的
2016年09月08日 04点09分
@139457820 我说的是自己重载的运算符,不是系统内建的[喷]
2016年09月08日 05点09分
@黄金止手 。。所以说,简单的我的意思,你若觉得慢了,大概就是你没有善用
2016年09月08日 05点09分
level 1
黄金止手 楼主
@139457820
type
BigInt=array [0..1000] of LongInt;
const
BigRadix=1000000000;
BigWidth=9;
operator := (x:LongInt) b:BigInt;
begin
//writeln(HexStr(@b));
if x>=BigRadix then
begin
b[0]:=2;
b[1]:=x mod BigRadix;
b[2]:=x div BigRadix;
end
else
begin
b[0]:=1;
b[1]:=x;
end;
end;
var
bn:BigInt;
begin
//writeln(HexStr(@bn));
bn:=1024;
end.
FPC 3.0.0 ,Win32 平台下,若去掉输出地址的注释,会看到那两个地址输出是不同的。
bn 在全局区, b 在栈,从而 b 不是 bn 的引用。
然后查看反汇编,会看到这个东西:
$00401491: mov $0x40a000,%edi
$00401496: lea -0xfa4(%e
bp
),%esi
$0040149C: mov $0xfa4,%ecx
$004014A1: rep movsb %ds:(%esi),%es:(%edi)
这就是把栈上的 b (函数返回值)逐字节复制给全局的 bn 。
2016年09月08日 06点09分 12
唉,一不小心全删了。。 总之你要拿效率来说,,因为Pascal毕竟是有优化的。你在真实情况下测算重载和非重载的差距,,我估计非重载10s的话重载不会超过11s
2016年09月08日 07点09分
@139457820 倒不是说具体效率不能接受。毕竟复制本身和高精度加法的时间相比还是短一些的。我只是觉得有复制就有点怪了。另外就是假如要被赋值的东西是大小20MB的数组(虽然实际上一般不会这样),那么默认情况下赋值就会直接爆栈……
2016年09月08日 07点09分
1