方法从类中孕育出来后,出现的时候,就已经狂奔了。因为没有调用一个方法的限制。
object_name.method_name
在其他语言中
object_name.method_name() 才是策马扬鞭
Lamda 似乎是语言特性中后加上去的,感觉很别扭。
代码块,似乎为了好看才加上去的。
Ruby 太自由了,自由是有代价的。
- -。调用方法时可以省去括号有时的确会把人搞糊涂,但是它的好处在写 DSL 是体现的很明显,看起来非常直接,就像语言内置了这类关键字一样。而且这点不是 Ruby 的特有的,lua,coffe script 等也是这样的。。。
对的,这是 Ruby 不好的地方。
我就喜欢不好的地方...
ruby 里的 .
总是发消息而不是取成员,没有二义性,是否带括号完全不重要.
javascript 里的 .
总是取成员而不是调用,如果一个成员是 function, 然后忘记写括号就败了。所以 coffee 里才不得不对无参函数调用加空括号。我觉得,强迫人写括号就是法西斯...
还有一些语言里 .
是带二义性的,只能靠后面的括号区分,这才是"野马".
lambda 是后来加上的,然后呢... 用 lisp 会感觉 lambda 比 ruby 好写么?
代码块是控制流不是 lambda. 你可以详细学学 smalltalk 是怎么整的就明白 block 的强大之处了...
当然代码块和 lambda 有相似之处,因为 closure 和 object 殊途同归。但是写久了以后就会发现是没有 silver bullet 的,二者在不同场景各有其用处...
@alsotang 我不同意。在平时的开发中,把函数当做参数传递的情况比较少,函数调用的情况很多。 并且显式使用 () 会有一个问题,忘记打 () 造成返回函数而不是调用函数,产生 bug。在 coffeescript 里面经常出现这样的状况。
有人对带不带括号感到无所适从,大概是被类似这样的 java / c# 代码坑过:
public class C {
public int foo = 3;
public int foo() {
return 4;
}
}
名字都是 foo
, 类型也一样,带和不带括号却竟然是两种东西,WTF! 一不小心多写了或者少写括号还不容易找出来.
ruby 里就没这种坑,不管带不带括号,名字相同的就是一个东西。
只能说 LZ 的比喻有点没读懂。
class Foo
def bar
puts 'bar'
end
end
f = Foo.new
f.method(:bar).call
Foo.instance_method(:bar).bind(f).call
#23 楼 @iBachue 无法否认,这种情形下 Python 的写法打字更少。
但是在现实的充分 OO 的代码中,使用协作者对象,要比使用从协作者对象身上"取下来"的已绑定的方法直观得多。更不要说取未绑定的方法,然后再伺机把未绑定方法所属的类的实例传进去调用 (你看用文字描述就这么费劲,更不要说后来人阅读代码时会有多大的认知障碍了).
如果你确实面对着需要频繁使用方法对象的场景,那么我无可辩驳。对我来说,目前能迅速回想起来的,Ruby 的方案对我而言更优越的用例有这么两个:
Getter API 演变。
比如说我们有个 UserPresenter 类 (在 fat model 的风格下很可能会是 User 类), 需要提供 avatar 属性返回头像 URL. 习惯上会觉得"啊既然是返回个 URL 字符串那么就用属性 getter 好了". 接下来需求变更,avatar 可能会有需要多个不同的大小。之前所需的尺寸只是最常用的而已。对 Ruby 而言这个状况很容易解决:把 avatar 方法实现成多接受一个参数来制定尺寸即可。而反观 Python, 假如把属性 getter 换成方法,旧的代码都要逐个改掉。如果不换,就没法制定尺寸参数。这个问题在我现在面对的 codebase 里就真实地存在着。
Stub
写隔离测试时,stub 是个常用的解除依赖的手段。Python 对象想要做 stub, 就要考虑"这个要 stub 的东西的获取方式是属性还是方法?". 而且在面对这种情况时:
class Foo(object):
@property
def bar(self):
return 'bar'
def test_foo(monkeypatch):
foo = Foo()
monkeypatch.setattr(foo, 'bar', 'bar')
assert foo.bar == 'bar'
在 monkeypatch.setattr(foo, 'bar', 'bar')
处会抛异常。虽说这种情形 stub 的方法也不是彻底没有,我正在用这个库. 但瞄了一眼具体实现,发现背后全是黑魔法。项目也不再维护了。用着多少有点担心。以及,这个库在 stub 时,所使用的预设返回值不能是 Mock 的实例。全是令人头疼的因素。而这些问题在 Ruby 的设计中根本不存在。
以及可能还有更多例子,但一时也想不起来了。就写这么两个吧。
不过,我依然无法下结论说 Python 和 Ruby 谁的方案更"好". 我只能说对于我个人的编程习惯和面对的问题领域而言,Ruby 的设计更适合我而已。
EDIT: 忘了说,前面 Python 代码示例中的 monkeypatch 是来自 py.test 测试框架的一个自带的插件 (在 pytest 的术语中属于 fixture 插件): http://pytest.org/latest/monkeypatch.html
从 FP 的角度来看,无括号的方法调用相当于取值的过程,中间没有副作用改变改变程序的状态,所以不需要分辨取值还是调用方法,因为结果都是一样的,但是 ruby 并没有这种约定。这方面 scala 就做得不错,有副作用的函数约定加上括号。
object_name.method_name()才是策马扬鞭
看不懂,楼主是吐槽 () 可以省略会跟对象的“属性”混淆吗?ruby 没有“属性”这一说法,只有方法,看似属性的,其实都只是其成员变量的读方法。另外,如果是要得到该方法的对象,可以用object_name.method(:method_name)
,这个只是语言特性,适应就好。
顺便,我觉得还是得稍微说明下:把前面 #17 楼 的代码换成 Python 的大致等价物:
class Foo(object):
def bar(self):
return 'bar'
接下来 Foo.bar
返回的不是函数,是未绑定的方法 Foo.bar(1)
并不会返回 'bar'
, 而是会抛异常。
Foo().bar
返回的也不是函数,是已绑定的方法。
所以我所理解的 Python 在"函数是一等公民"这个特性上更优秀的地方,仅在于函数/绑定方法/未绑定的方法的调用方式是与常规的方法调用的语法完全相同的,都是后面直接加一对括号。而 Ruby 更加地以对象为中心,就得用 func.call()
或 func.()
.
"函数是第一公民"我还是觉得有。写 func.call() 和写 func.() 虽然都不及 func() 简洁,但我觉得不能说没有。
个人觉得"函数是第一公民"的特性首先体现在取出函数对象并赋值上,Python 的写法是foo.bar
或是Foo.bar
,而 Ruby 的那种手法,调方法才能取出函数,和 Python 比起来,额,差太远了,我因此就不觉得 Ruby 还能被称为"函数是第一公民"的语言了,只能算是一等半吧。。。。
scala 有没有括号和有没有副作用完全没联系吧...
object O {
def prop = {
println("side effect")
2
}
def main(xs: Array[String]) {
O.prop
O.prop
}
}
另外 scala 函数后面的括号又和其他语言不同,它其实是 tuple 来的...
object O {
def f(x:Int) = {
x
}
def main(xs: Array[String]) {
O.f(1)
val x = (1)
O f x
}
}
这时候你甚至会怀念 java... 至少 O.f
和 O.f()
还能看出区别,但 scala 里就看不出来了...
隔离了副作用的只有 haskell 能做到,scala 那种大杂烩是不可能语法隔离副作用的...
再另外 gcc 有告诉编译器函数无副作用的 attribute, 例如一个求平方的函数可以这么标记
int square (int) __attribute__ ((pure));
那编译器就知道它是 pure 函数,下面的代码
int x = square(3);
int y = square(3);
就会被优化成类似
int x = square(3);
int y = x; // pure 函数无副作用, 相同参数结果也相同, 不需要再调用一次了
the python way
def f(g):
g()
def bar():
print("hello world")
f(bar)
the ruby way
def f
yield
end
def bar
print "hello world"
end
f { bar }
括号加冒号字符数刚好等于 end
...
def f(a, b):
a()
b()
def foo():
print('foo')
def bar():
print('bar')
f(foo, bar)
你这个是 corner case ... 可以用 closure = object 的哲♂学变换嘛
def f a, b
send a
send b
end
def foo
print 'foo'
end
def bar
print 'bar'
end
f :foo, :bar