[转帖]Ruby动态特性 可调用对象_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
2
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3615 | 回复: 1   主题: [转帖]Ruby动态特性 可调用对象        下一篇 
zhongwei.wang
注册用户
等级:中校
经验:1859
发帖:60
精华:1
注册:2013-12-23
状态:离线
发送短消息息给zhongwei.wang 加好友    发送短消息息给zhongwei.wang 发消息
发表于: IP:您无权察看 2013-12-27 9:20:51 | [全部帖] [楼主帖] 楼主

可调用对象: 
可调用对象是一个对象,可以给该对象发送call消息,让它执行定义在其中(通常是在一个代码块中)的一些代码。Ruby中主要的可调用对象是方法、Proc对象和lambda。 
Proc对象 
用一个代码块来实例化Proc类,可以产生一个Proc对象。 

pr = Proc.new {puts "Inside a Proc's block"}


这里的代码块是不是会马上执行,只是作为Proc对象的一个定义体保存了下来。如果要执行它,就要发送call消息给这个对象。 

pr.call #Inside a Proc's block


作为闭包的Proc对象 
首先我们应该清楚:方法定义体中的局部变量和方法调用作用域中的局部变量是两回事,它们毫不相干。 
Proc对象的作用域有所不同。Proc.new调用发生时作用域中的局部变量仍然在作用域中。也就是说,不论何时何地调用该对象,那些变量始终是在Proc对象的作用域中。 

def call_some_proc(pr)
a = "irrelevant 'a' in method scope"
puts a
pr.call
end
a = "'a' to be used in Proc block"
pr = Proc.new{puts a}
pr.call
call_some_proc(pr)


输出结果: 

'a' to be used in Proc block
irrelevant 'a' in method scope
'a' to be used in Proc block


来分析一下这个代码。先看在外围作用域中也有一个变量a,还建立了一个过程对象。接着调用这个过程对象,这时就会输出结果" 'a' to be used in Proc block ",最后是一个方法调用,参数是一个Proc对象pr.现在进入方法call_some_pro中,方法体中有个变理a,再输出a的值,这时就会输出" irrelevant 'a' in method scope ",最后是一个pr.call的调用,由于pr里保存了在外围作用域中的a,所以这里输出的结果仍是外围a的值" 'a' to be used in Proc block "。 
像这样带着产生它的上下文信息的一段代码,称为闭包。 
Proc对象的参数 
Proc创建对象时,代码块中也可以提供接收参数。 

>> pr = Proc.new { |x| puts "Call with argument #{x}" }
>> pr.call(100) #Call with argument 100


Proc对象处理它们的参数很微妙,如果给它提供的参数多于定义的个数,那么将得到一个警告。如果没有提供参数,那么该参数变量会被初始为nil,同样也会有一个警告。 
如果Proc对象接受多个参数,那么调用它时所提供的参数被赋值给参数列表中的变量。多余的参数被忽略。 
也可以使用星号操作符(*)将所有的参数合并为一个参数(数组): 

pr = Proc.new{|*x| p x}
pr.call
pr.call(1)
pr.call(1,2)


输出: 

[]
[1]
[1, 2]


如果接受多个参数,带星号的参数要放在最后,合并剩余的参数。 

pr = Proc.new { |x, *y| p x, y}
pr.call(1, 2, 3)


输出: 

1
[2, 3]


用lambda生成匿名函数 
lambda用来生成匿名函数,需要做的就是给lambda提供一个代码块,生成的对象其实也是Proc对象,同样可以给它发送call消息。 

>> lam = lambda { puts "A lambda" }
>> lam.call
=> A lambda


lambda生成的对象和所有Proc对象一样都是闭包。但是,lambda生成的Proc对象和Proc.new生成的Proc对象之间是差别的。 
lambda要比Proc.new严格: 

l_proc = lambda {|a, b| puts "a + b = #{a+b}"}
n_proc = lambda {|a, b| puts "a + b = #{a+b}"}
n_proc.call(1, 2, 3)
-> a + b = 3
l_proc.call(1, 2, 3)
-> !ArgumentError (wrong number of arguments (3 for 2))


lambda生成的Proc对象,在使用call调用时,如果参数不对会产生ArgumentError异常。而Proc.new生的Proc对象就不会。 
还有一个区别是它们返回值的方式不一样。Proc.new生成的Proc对象是从代码块返回的。 

def proc_new
new_proc = Proc.new { return "I got here..." }
new_proc.call
return "...but not here."
end
def lambda_proc
new_proc = lambda { return "You get here..." }
new_proc.call
return "And I got here!"
end
puts proc_new
-> I got here...
puts lambda_proc
-> And I got here!


再论代码块 
Ruby中没有Block类或者Block对象,代码块仅在语法意义上存在。代码块是出现在方法调用之后、由大括号(或者do...end)包围起来、等待着被使用的一些代码。 
在方法中可以将代码专转换成Proc对象。这可以通过给方法参数加上&来实现,并且这个加上&的变量是在参数的最后一项: 

def grab_block(&block)
block.call
end
grab_block { puts "This block will end up in the variable 'block'" }


同样的,使用 & 符号也可以反过来将Proc对象或lambda转换为一个代码块。 

lam = lambda { puts "This lambda will serve as a code block" }
grab_block &lam


下面是另一种写法: 

grab_block &lambda { puts "This lambda will serve as a code block" }


作为对象的方法 
最经常用的是方法,一般使用是间接进行的:给对象发送一条消息,然后对象执行相应的方法。其实方法也是对象,同样可以把它做为对象来处理。 
通过method方法,并以方法名作为参数(以字符串或符号的形式),就可以得到方法对象。 

class C
def talk
puts "Method-grabbing test! self is #{self}."
end
end
c = C.new
meth = c.method(:talk)


现在meth就是一个方法对象,而是一个绑定的方法对象,这里是绑定在对象c上的。通过给meth发送"call"消息运行。 

meth.call


输出结果: 

Method-grabbing test! self is #<C:0x35666>.


也可以将方法和它所绑定的对象解绑定(unbind),然后将它绑定(bind)到另一个对象上。只要这个对象和原来的对��是同一个类的实例(或者是子类的实例): 

class D < C
end
d = D.new
unbound = meth.unbind
unbound.bind(d).call


输出结果: 

Method-grabbing test! self is #<D:0x32d7bc>.


如果不想通过对已绑定的方法使用unbind来得到方法对象,而是直接得到解绑定的方法对象,则可以用instance_method方法,从类来得到。 

unbound = C.instance_method(:talk)


这样unbound就是一个解绑定对象,要使用unbound的话,就把它绑定到C的对象上就可以了。 

>> c = C.new
>> unbound.bind(c).call


绑定和解绑定的使用相对来说要少很多,能够读懂就够了。在某些场合,对于“怎么做”这个问题的最好答案是,“使用解绑定的方法”。 

class A
def a_method
puts "Definition in class A"
end
end
class B < A
def a_method
puts "Definition in class B(subclass of A)"
end
end
class C < B
end


然后获得一个C的实例: 

c = C.new


有没有办法叫最底层的实例(这里的c),接收到A类中的消息"a_method"呢? 
很显然,默认情况下是不行的,c就会执行它在方法查找路径上第一个可以匹配的方法: 

c.a_method


输出结果是: 

Definition in class B(subclass of A)


但是可以通过一个解绑定和一个绑定操作解决 

class C
def call_original
A.instance_method(:a_method).bind(self).call
end
end


现在直接在c上调用call_original就可以了。 
如果想要熟练的掌握Ruby的动态特性,就需要理解该技术,但尽量不要使用。 
method对象也是可调对象,并且,可以解开与它们的实例对象之间的绑定。

def prefix(s,len=1)
s[0,len]
end
p prefix("ruby")
p prefix("ruby",4)
;


也可以传递任意个参数,只需在参数前面加上*即可: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def max(first,*rest) 
  2.  max=first 
  3.  rest.each{|x| max=x if x>max} 
  4.  max 
  5. end 
  6. data = [1,3,6,4] 
  7. p max(*data) #first=1,rest=[3,6,4] -> 6 

def max(first,*rest)
max=first
rest.each{|x| max=x if x>max}
max
end
data = [1,3,6,4]
p max(*data) #first=1,rest=[3,6,4] -> 6
;


代码块也是个比较有意思的东西, 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def sequence(n,m,c) 
  2.  i=0 
  3.  while i<n 
  4.  yield i*m+c 
  5.  i+=1 
  6.  end 
  7. end 
  8. sequence(5,2,2){|x| puts x} 

def sequence(n,m,c)
i=0
while i<n
yield i*m+c
i+=1
end
end
sequence(5,2,2){|x| puts x}
;


这种语法也很特别,也没在其它语言里见过,通过yield来调用块里面的代码。 
代码块是ruby的一种句法结构,它们不是对象,也不能像对象一样操作,不过可以创建对象来表示一个代码块。根据对象的创建方式,叫做proc或者lambda。proc的行为与代码块类似,而lambda的行为则与方法类似,不过,它们都是Proc类的实例。 
用&做前缀的代码块参数把代码块和方法关联起来,如: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def makeproc(&p) 
  2.  p 
  3. end 
  4. adder=makeproc{|x,y| x+y} 
  5. p adder.call(1,2)#3 

def makeproc(&p)
p
end
adder=makeproc{|x,y| x+y}
p adder.call(1,2)#3
;


也可以直接用Proc.new创建proc对象,上面的代码等价于 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def makeproc  
  2.      Proc.new  
  3. end  

def makeproc
Proc.new
end
;


另一种创建Proc对象的方式是使用lambda方法,它是Kernel模块的一个方法,所以看不起来像是一个全局函数。就像名字所暗示的,lambda方法返回的Proc对象是一个lambda而非proc。lambda方法不带参数,但是在调用时必须关联一个代码块: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. succ=lambda{|x| x+1} 
  2. p succ.call(4) 

succ=lambda{|x| x+1}
p succ.call(4)
;


Proc还定义了数组访问操作符,它的工作方式与call一样,这意味可以用与方法调用类似的方式调用proc或lambda: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. p succ[5]  
  2. p adder[2,3]  

p succ[5]
p adder[2,3]
;


从这点说,ruby的确不是很正规,不过比较好玩。 
ruby里面也有闭包这个特性,这个在jdk1.7才有可能实现的特性,在ruby中早就有了,闭包的主要特点是能记住其被定义的上下文,并在被调用时使用该上下文。proc与lambda都是闭包。 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def multiplier(n) 
  2.  lambda{|data| data.collect{|x| x*n}} 
  3. end 
  4. doubler = multiplier(2) 
  5. puts doubler.call([1,2,3]) #打印输出2,4,6  


def multiplier(n)
lambda{|data| data.collect{|x| x*n}}
end
doubler = multiplier(2)
puts doubler.call([1,2,3]) #打印输出2,4,6
;


multiplier方法返回一个lambda,这个lambda在定义它的范围外被使用,我们称之为一个闭包,这个闭包封闭(或保持)了所绑定的方法参数n。 
Proc定义了一个名为binding的方法,调用这个方法会返回一个Binding对象,它表示该闭包所使用的绑定。 
闭包与绑定一起使用,大多数时候变得很简单,Binding对象本身并没有什么值得注意的方法,不过它可以作为全局函数eval的第二个参数,能为eval函数执行给定代码时提供一个上下文环境。在Ruby1.9中,Binding也自带了eval方法,你可能更愿意直接使用它。 
使用Binding对象和eval方法可以让我们获得一个操控闭包行为的后门: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def multiplier(n) 
  2.  lambda{|data| data.collect{|x| x*n}} 
  3. end 
  4. doubler = multiplier(2) 
  5. puts doubler.call([1,2,3]) #打印输出2,4,6  
  6. eval("n=3",doubler.binding)  
  7. puts doubler.call([1,2,3]) #打印3,6,9  


def multiplier(n)
lambda{|data| data.collect{|x| x*n}}
end
doubler = multiplier(2)
puts doubler.call([1,2,3]) #打印输出2,4,6
eval("n=3",doubler.binding)
puts doubler.call([1,2,3]) #打印3,6,9
;


一种更快捷的方式是直接使用eval方法:eval("n=3",doubler)。 

def prefix(s,len=1)
s[0,len]
end
p prefix("ruby")
p prefix("ruby",4)
;


也可以传递任意个参数,只需在参数前面加上*即可: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def max(first,*rest) 
  2.  max=first 
  3.  rest.each{|x| max=x if x>max} 
  4.  max 
  5. end 
  6. data = [1,3,6,4] 
  7. p max(*data) #first=1,rest=[3,6,4] -> 6 

def max(first,*rest)
max=first
rest.each{|x| max=x if x>max}
max
end
data = [1,3,6,4]
p max(*data) #first=1,rest=[3,6,4] -> 6
;


代码块也是个比较有意思的东西, 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def sequence(n,m,c) 
  2.  i=0 
  3.  while i<n 
  4.  yield i*m+c 
  5.  i+=1 
  6.  end 
  7. end 
  8. sequence(5,2,2){|x| puts x} 

def sequence(n,m,c)
i=0
while i<n
yield i*m+c
i+=1
end
end
sequence(5,2,2){|x| puts x}
;


这种语法也很特别,也没在其它语言里见过,通过yield来调用块里面的代码。 
代码块是ruby的一种句法结构,它们不是对象,也不能像对象一样操作,不过可以创建对象来表示一个代码块。根据对象的创建方式,叫做proc或者lambda。proc的行为与代码块类似,而lambda的行为则与方法类似,不过,它们都是Proc类的实例。 
用&做前缀的代码块参数把代码块和方法关联起来,如: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def makeproc(&p) 
  2.  p 
  3. end 
  4. adder=makeproc{|x,y| x+y} 
  5. p adder.call(1,2)#3 

def makeproc(&p)
p
end
adder=makeproc{|x,y| x+y}
p adder.call(1,2)#3
;


也可以直接用Proc.new创建proc对象,上面的代码等价于 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def makeproc  
  2.      Proc.new  
  3. end  

def makeproc
Proc.new
end
;


另一种创建Proc对象的方式是使用lambda方法,它是Kernel模块的一个方法,所以看不起来像是一个全局函数。就像名字所暗示的,lambda方法返回的Proc对象是一个lambda而非proc。lambda方法不带参数,但是在调用时必须关联一个代码块: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. succ=lambda{|x| x+1} 
  2. p succ.call(4) 

succ=lambda{|x| x+1}
p succ.call(4)
;


Proc还定义了数组访问操作符,它的工作方式与call一样,这意味可以用与方法调用类似的方式调用proc或lambda: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. p succ[5]  
  2. p adder[2,3]  

p succ[5]
p adder[2,3]
;


从这点说,ruby的确不是很正规,不过比较好玩。 
ruby里面也有闭包这个特性,这个在jdk1.7才有可能实现的特性,在ruby中早就有了,闭包的主要特点是能记住其被定义的上下文,并在被调用时使用该上下文。proc与lambda都是闭包。 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def multiplier(n) 
  2.  lambda{|data| data.collect{|x| x*n}} 
  3. end 
  4. doubler = multiplier(2) 
  5. puts doubler.call([1,2,3]) #打印输出2,4,6  


def multiplier(n)
lambda{|data| data.collect{|x| x*n}}
end
doubler = multiplier(2)
puts doubler.call([1,2,3]) #打印输出2,4,6
;


multiplier方法返回一个lambda,这个lambda在定义它的范围外被使用,我们称之为一个闭包,这个闭包封闭(或保持)了所绑定的方法参数n。 
Proc定义了一个名为binding的方法,调用这个方法会返回一个Binding对象,它表示该闭包所使用的绑定。 
闭包与绑定一起使用,大多数时候变得很简单,Binding对象本身并没有什么值得注意的方法,不过它可以作为全局函数eval的第二个参数,能为eval函数执行给定代码时提供一个上下文环境。在Ruby1.9中,Binding也自带了eval方法,你可能更愿意直接使用它。 
使用Binding对象和eval方法可以让我们获得一个操控闭包行为的后门: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def multiplier(n) 
  2.  lambda{|data| data.collect{|x| x*n}} 
  3. end 
  4. doubler = multiplier(2) 
  5. puts doubler.call([1,2,3]) #打印输出2,4,6  
  6. eval("n=3",doubler.binding)  
  7. puts doubler.call([1,2,3]) #打印3,6,9  


def multiplier(n)
lambda{|data| data.collect{|x| x*n}}
end
doubler = multiplier(2)
puts doubler.call([1,2,3]) #打印输出2,4,6
eval("n=3",doubler.binding)
puts doubler.call([1,2,3]) #打印3,6,9
;


一种更快捷的方式是直接使用eval方法:eval("n=3",doubler)。 

def prefix(s,len=1)
s[0,len]
end
p prefix("ruby")
p prefix("ruby",4)
;


也可以传递任意个参数,只需在参数前面加上*即可: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def max(first,*rest) 
  2.  max=first 
  3.  rest.each{|x| max=x if x>max} 
  4.  max 
  5. end 
  6. data = [1,3,6,4] 
  7. p max(*data) #first=1,rest=[3,6,4] -> 6 

def max(first,*rest)
max=first
rest.each{|x| max=x if x>max}
max
end
data = [1,3,6,4]
p max(*data) #first=1,rest=[3,6,4] -> 6
;


代码块也是个比较有意思的东西, 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def sequence(n,m,c) 
  2.  i=0 
  3.  while i<n 
  4.  yield i*m+c 
  5.  i+=1 
  6.  end 
  7. end 
  8. sequence(5,2,2){|x| puts x} 

def sequence(n,m,c)
i=0
while i<n
yield i*m+c
i+=1
end
end
sequence(5,2,2){|x| puts x}
;


这种语法也很特别,也没在其它语言里见过,通过yield来调用块里面的代码。 
代码块是ruby的一种句法结构,它们不是对象,也不能像对象一样操作,不过可以创建对象来表示一个代码块。根据对象的创建方式,叫做proc或者lambda。proc的行为与代码块类似,而lambda的行为则与方法类似,不过,它们都是Proc类的实例。 
用&做前缀的代码块参数把代码块和方法关联起来,如: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def makeproc(&p) 
  2.  p 
  3. end 
  4. adder=makeproc{|x,y| x+y} 
  5. p adder.call(1,2)#3 

def makeproc(&p)
p
end
adder=makeproc{|x,y| x+y}
p adder.call(1,2)#3
;


也可以直接用Proc.new创建proc对象,上面的代码等价于 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def makeproc  
  2.      Proc.new  
  3. end  

def makeproc
Proc.new
end
;


另一种创建Proc对象的方式是使用lambda方法,它是Kernel模块的一个方法,所以看不起来像是一个全局函数。就像名字所暗示的,lambda方法返回的Proc对象是一个lambda而非proc。lambda方法不带参数,但是在调用时必须关联一个代码块: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. succ=lambda{|x| x+1} 
  2. p succ.call(4) 

succ=lambda{|x| x+1}
p succ.call(4)
;


Proc还定义了数组访问操作符,它的工作方式与call一样,这意味可以用与方法调用类似的方式调用proc或lambda: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. p succ[5]  
  2. p adder[2,3]  

p succ[5]
p adder[2,3]
;


从这点说,ruby的确不是很正规,不过比较好玩。 
ruby里面也有闭包这个特性,这个在jdk1.7才有可能实现的特性,在ruby中早就有了,闭包的主要特点是能记住其被定义的上下文,并在被调用时使用该上下文。proc与lambda都是闭包。 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def multiplier(n) 
  2.  lambda{|data| data.collect{|x| x*n}} 
  3. end 
  4. doubler = multiplier(2) 
  5. puts doubler.call([1,2,3]) #打印输出2,4,6  


def multiplier(n)
lambda{|data| data.collect{|x| x*n}}
end
doubler = multiplier(2)
puts doubler.call([1,2,3]) #打印输出2,4,6
;


multiplier方法返回一个lambda,这个lambda在定义它的范围外被使用,我们称之为一个闭包,这个闭包封闭(或保持)了所绑定的方法参数n。 
Proc定义了一个名为binding的方法,调用这个方法会返回一个Binding对象,它表示该闭包所使用的绑定。 
闭包与绑定一起使用,大多数时候变得很简单,Binding对象本身并没有什么值得注意的方法,不过它可以作为全局函数eval的第二个参数,能为eval函数执行给定代码时提供一个上下文环境。在Ruby1.9中,Binding也自带了eval方法,你可能更愿意直接使用它。 
使用Binding对象和eval方法可以让我们获得一个操控闭包行为的后门: 

Ruby代码  北京联动北方科技有限公司北京联动北方科技有限公司北京联动北方科技有限公司

  1. def multiplier(n) 
  2.  lambda{|data| data.collect{|x| x*n}} 
  3. end 
  4. doubler = multiplier(2) 
  5. puts doubler.call([1,2,3]) #打印输出2,4,6  
  6. eval("n=3",doubler.binding)  
  7. puts doubler.call([1,2,3]) #打印3,6,9  


def multiplier(n)
lambda{|data| data.collect{|x| x*n}}
end
doubler = multiplier(2)
puts doubler.call([1,2,3]) #打印输出2,4,6
eval("n=3",doubler.binding)
puts doubler.call([1,2,3]) #打印3,6,9
;


一种更快捷的方式是直接使用eval方法:eval("n=3",doubler)。 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;




赞(0)    操作        顶端 
zhongwei.wang
注册用户
等级:中校
经验:1859
发帖:60
精华:1
注册:2013-12-23
状态:离线
发送短消息息给zhongwei.wang 加好友    发送短消息息给zhongwei.wang 发消息
发表于: IP:您无权察看 2013-12-27 9:29:05 | [全部帖] [楼主帖] 2  楼

提示:本文章在发帖的时候,不知道为什么预览的时候是好的,但是发出来后就和上一个帖子混在一起了,我会重新发一下。



赞(0)    操作        顶端 
总帖数
2
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论