Rails

Rails 使用 carrierwave 上传文件

Posted on

……崩溃,找了很久的资料,最后找到一份template才解决问题。

carrierwave是一个rails 上传文件的 gem,然而用不来……
作者在 github 上的文档真的很没有条理性

前言

偶然间看到这篇文章,希望读者也可以多思考一下。
现在框架发展这么迅速,各种api泛滥,我们在寻求解决一个问题的时候,往往会有点急功近利。如果你考虑了底层实现,那你才是真正学到了东西,并能广泛运用它。否则,你顶多只是积累了当前框架或者api的使用经验。换了一个框架你就完全不会了。

但我的确是看完了这篇文章,我也的确去思考了,但是还是不会用!!!
因为网上很多文章和博客都忽略了一个非常重要的地方,下面我会说明。

这里详细介绍一下 carrierwave 的用法,并假设你已经开了上面那篇文章并思考过了。

安装

首先是安装,在gemfile中输入 gem 'carrierwave', '~> 1.0' 并执行 bundle install

carrierwave 实际建立的是 model ,但这里便于区分并且生成的类同时也是 uploader ,输入命令

rails generate uploader Image

这里的 Image 是由你自己取名的 uploader ,输完后就会生成一个 ruby文件,关于文件上传的一些设置一般都是在这个文件中。

app/uploaders/image_uploader.rb

View

根据MVC,我们先写 views ,一般是在某个目录下的 new.html.erb

<%= form_for @comic, :url => comics_path,:html => {:multipart => true} do |t|%>
# 其他代码

    <br><%= t.label :image, '漫画封面' %></br>
    <%= t.file_field :image %>
    <%= t.hidden_field :image_cache %>

# 其他代码
<% end %>

这里一定一定一定要注意, :html => { :multipart => true } 这个hash一定要加!!!只有加了这个才能上传文件,别问我什么意思,什么道理,我不知道……

Controller

然后是controller,一般为 某个目录下的 create 函数
其实在controller只要和平常一样操作即可,这里还是放上我写的吧

  def create
    @comic = Comic.new(comic_params)

    if @comic.save
        # 其他代码
    else
        # 其他代码
    end
  end

Uploader 的 其他配置

太复杂的高级用法我就不说了,更何况我自己也不会

在预生成的 *_uploader.rb 文件中, 你可以通过重载若干函数来实现你的自定义

store_dir

非重载函数,意思为上传目录,上传目录的相对路径根目录在 public,路径问题上你可以自己设置

extension_whitelist

重载函数,意思为上传文件白名单,默认为所有文件

filename

重载函数,用途为更改文件名,默认和 变量 original_name 相同

default_url

重载函数,虽然没用过,但是看文档还是看懂了,意为默认文件,如果你没有上传任意文件,就将你设置的 default_url 传给你 new 的对象

其他就不是很清楚了。

图片的使用

因为在我的课设中只会上传图片,这里说一下上传图片的使用

因为新增加的类是 uploader ,它实质上有很多属性,比如,filename, original_name, store_dir, url 等等
,而我们显示图片其实只需要一个 url 即可。

在 View 中输入

<%= image_tag comic.image_url %>

就可以显示当前 comic 的 image

树形DP

CodeForces 842 C Ilya And The Tree

Posted on

树上的选择性DP……
不是很懂其他人的做法,偶然间看到有人用ruby A了,我也就用ruby敲了一蛤
速度有点玄……

题意:
给你一棵树,每个节点都有权值,要你求每个节点到根节点所形成的树链 gcd。
其中每条树链都可以选择一个节点使得权值变为 0 。

思路:
感觉这就是裸题……
只不过就是把数组改成树上了而已。
用一个栈或者队列去遍历这棵树,对于每个节点是否变0都考虑一下就好了……

AC Code

#!/usr/bin/env ruby
# encoding: utf-8
n = gets.to_i
a = gets.split.map(&:to_i)
g = Array.new(n) {[]}

(n-1).times do
    u, v = gets.split.map(&:to_i)
    u, v = u-1, v-1
    g[u] << v
    g[v] << u
end

def GCD(a, b) return b == 0 ? a : GCD(b, a%b) end

dp = Array.new(n) { Array.new(2) { [] } }
dp[0] = [[a[0]], [0]]
par = [-1]*n

st = [0]
until st.empty?
    u = st.pop
    g[u].each do |v|
        next if v == par[u]
        par[v] = u
        st.push(v)
        dp[v][0] += dp[u][0].map{|x| GCD(x,a[v])}
        dp[v][1] += dp[u][0]
        dp[v][1] += dp[u][1].map{|x| GCD(x,a[v]) }
        2.times do |i|
            dp[v][i].uniq!
        end
    end
end

print a[0]
for u in 1...n do
    print " #{dp[u][1].max}"
end
Ruby

CodeForces 832 B Petya and Exam

Posted on

比赛时候用c++ 搞了一个小时还没出来,结束后用ruby写了个正则改了两次就过了……

真的是崩溃……

题意:
?替代任意好字符,* 替代任意数目的坏字符

思路:
正则瞎搞

AC Code

good = gets.to_str.chomp
pattern = gets.to_str
key = pattern.strip.gsub("?","["+good+"]").gsub("*","[^"+good+"]*")
rg = Regexp.new("^"+key+"$")

n = gets.to_i
n.times do
    buf = gets.to_str
    if rg =~ buf
        puts "YES"
    else
        puts "NO"
    end
end
Ruby

CodeForces 195 C 上班题

Posted on

别说了,上班题,我比赛用c++ 写了一个半小时,晚上继续用c++写了两个小时,突然跟天海吹牛说,要是我会c++的正则表达式,这道题,妙了!
然后就说可以用脚本语言,我也只能勉为其难的用ruby写了。

推荐一个ruby在线正则表达式检测 网站

语法有些忘了……写的慢了点

题意:
模拟 try catch

思路:
无。

AC Code

#!/usr/bin/env ruby
# encoding: utf-8
n=gets.to_i

thlist = []
thid = []
trylist = []
flag=0

n.times do |id|
  str=gets.strip!
  next if flag == 1
  if /^try$/ =~ str
    trylist << id
  elsif /throw/ =~ str
    /(?<=\()\s*\w+\s*(?=\))/ =~ str
    thlist << $&.strip
    thid << id
  elsif /catch/ =~ str
    /(?<=\()\s*\w+\s*(?=,)/ =~ str
    if (pos=thlist.index($&.strip))!=nil && thid[pos] > trylist.last
      /(?<=\").+(?=\")/ =~ str
      puts $&
      flag=1
    end
    trylist.pop
  end
end

puts "Unhandled Exception" if flag == 0
Ruby

数值计算编程作业(Ruby版)

Posted on

内容包括

  1. 二分法求解二元一次方程的根
  2. 牛顿法求解一元多次方程的根
  3. 顺序消去法解线性方程组
  4. 列选主元消去法解线性方程组
  5. 全选主元消去法解线性方程组
  6. Doolittle分解线性方程组
  7. Crout分解线性方程组
  8. 平方根法求线性方程组
  9. 拉格朗日插值法
  10. 牛顿插值法
  11. 最小二乘法——线性拟合
  12. 变步长梯形求积算法计算积分
  13. 龙贝格算法计算积分
  14. 改进欧拉算法求常微分方程的数值解
  15. 四阶龙格-库塔法求常微分方程的数值解

累!!!!!!!不详细介绍了

#!/usr/bin/env ruby
# encoding: utf-8

$eps=1e-8
$inf=0x3f3f3f3f

class TwoSplit
  def initialize
    @a,@b,@c,@d=2,4,2,18
  end

  def getVal(x)
    @a*x*x+@b*x+@c-@d
  end
  private :getVal

  def three_split
    le,ri,mid=-$inf,$inf,1
    while le+$eps<ri
      mid=(le+ri)/2
      if(getVal(mid)+$eps<0)
        le=mid+$eps
      else
        ri=mid-$eps
      end
    end
    mid
  end
  private :three_split

  def two_split(le,ri)
    mid=1
    while le+$eps<ri
      mid=(le+ri)/2
      if(getVal(mid)<0)
        le=mid+$eps
      else
        ri=mid-$eps
      end
    end
    mid
  end
  private :two_split

  def calculate_ans
    @a,@b,@c,@d=-@a,-@b,-@c,-@d if (@a<0)
    k=three_split
    if(getVal(k)>0)
      puts 'No Answer'
    else
      rans=two_split(k,1.0*$inf)
      lans=2*k-rans
      puts '测试方程为2x^2+4x+2=18'
      printf("方程解为:%.2f\n",lans)
      printf(" %.2f",rans) if((lans-rans).abs>$eps*10)
      puts ''
    end
  end
end

class NewtonIterate
  def initialize
    @a,@b,@c,@d=-2,-5,1,3
  end

  def get_val(x)
    @a*x*x+@b*x+@c-@d
  end

  def get_der(x)
    2*@a*x+@b
  end

  def newton(val)
    pre=val
    val=pre-get_val(pre)/get_der(pre)
    while((pre-val).abs>$eps)
      pre=val
      val=pre-get_val(pre)/get_der(pre)
    end
    val
  end

  def calculate_ans
    if(@b*@b-4*@a*@c<0)
      puts 'No Answer'
    else
      lans=newton(-1.0*$inf)
      rans=newton(1.0*$inf)
      puts '求解方程为-2x^2-5x+1=3'
      printf("方程解为:%.2f",lans);
      printf(" %.2f",rans) if((lans-rans).abs>$eps)
      puts ''
    end
  end
end

class ResolveMat
  def initialize
    @mat=6.times.map{ [0] * 6 }
    @l=6.times.map{ [0] * 6 }
    @u=6.times.map{ [0] * 6 }
    @x,@y=[0]*6,[0]*6
    @b=[1,2,3,4]
    @mat[0]=[2,10,4,-3]
    @mat[1]=[-3,-4,-12,13]
    @mat[2]=[1,2,6,-4]
    @mat[3]=[4,14,9,-13]
    @n=4
  end

  def print_mat(mat)
    yield
    @n.times do |i|
      print '['
      @n.times do |j|
        if((mat[i][j]-0).abs > $eps)
          printf("%8.3f ", mat[i][j]);
        else
          printf("         ");
        end
      end
      puts ']'
    end
    puts ''
  end

  def doolittle
    @n.times { |i| @l[i][i]=1 }

    @n.times do |k|
      k.upto(@n-1) do |j|
        @u[k][j]=@mat[k][j]
        k.times { |i| @u[k][j]-=(@l[k][i]*@u[i][j]) }
      end

      (k+1).upto(@n-1) do |i|
        @l[i][k]=@mat[i][k]
        k.times { |j| @l[i][k]-=(@l[i][j]*@u[j][k]) }
        @l[i][k]/=1.0*@u[k][k]
      end
    end

    @n.times do |i|
      @y[i]=@b[i]
      i.times { |j| @y[i]-=(@l[i][j]*@y[j]) }
    end

    (@n-1).downto(0) do |i|
      @x[i]=@y[i]
      (i+1).upto(@n-1) { |j| @x[i]-=(@u[i][j]*@x[j]) }
      @x[i]/=1.0*@u[i][i]
    end
    puts 'Doolittle分解矩阵'
    print_mat(@mat) {puts '原矩阵为:'}
    print_mat(@l) {puts '矩阵L为:'}
    print_mat(@u) {puts '矩阵U为:'}
  end

  def crout
    @n.times { |i| @u[i][i]=1 }

    @n.times do |k|
      k.upto(@n-1) do |i|
        @l[i][k]=@mat[i][k]
        k.times { |j| @l[i][k]-=(@l[i][j]*@u[j][k]) }
      end
      (k+1).upto(@n-1) do |i|
        @u[k][i]=@mat[k][i]
        k.times { |j| @u[k][i]-=(@l[k][j]*@u[j][i]) }
        @u[k][i]/=1.0*@l[k][k]
      end
    end

    @n.times do |i|
      @y[i]=@b[i]
      i.times { |j| @y[i]-=(@l[i][j]*@y[j]) }
      @y[i]/=1.0*@l[i][i]
    end
    (@n-1).downto(0) do |i|
      @x[i]=@y[i]
      (i+1).upto(@n-1) { |j| @x[i]-=(@u[i][j]*@x[j]) }
      # @x[i]/=@u[i][i]
    end
    puts 'Crout分解矩阵'
    print_mat(@mat) {puts '原矩阵为:'}
    print_mat(@l) {puts '矩阵L为:'}
    print_mat(@u) {puts '矩阵U为:'}
  end

  def square #待验证
    t=6.times.map { [0]*6 }
    lt=6.times.map { [0]*6 }
    d=6.times.map { [0]*6 }

    @n.times { |i| lt[i][i]=1 }
    t[0][0]=@mat[0][0]

    1.upto(@n-1) do |i|
      t[i][0] = @mat[i][0]
      lt[0][i] = @mat[0][i] / t[0][0]
    end

    1.upto(@n-1) do |i|
      1.upto(i) do |j|
        t[i][0]=@mat[i][0]
        j.times { |k| t[i][j]-=t[i][k]*lt[k][j] }
      end

      (i+1).upto(@n-1) do |j|
        lt[i][j] = @mat[i][j]
        i.times {|k| lt[i][j] = lt[i][j] - t[i][k] * lt[k][j] }
        lt[i][j] = lt[i][j] / t[i][i]
      end
    end

    @n.times do |i|
       @n.times do |j|
         @l[i][j]=lt[i][j]
         d[i][j]=0 if i!=j
         d[i][j]=t[i][j] if i==j
       end
    end

    @y[0]=@b[0]
    1.upto(@n-1) do |i|
      i.times.reduce(0.0) { |sum,k| @y[i]= @b[i]-(sum+=lt[k][i]*@y[k]) }
      # m=0.0
    end

    @x[@n-1]=@y[@n-1]/t[@n-1][@n-1]
    (@n-1).downto(0) do |i|
      (i+1).upto(@n-1).reduce(0.0)  { |sum,k|  @x[i]=@y[i]/d[i][i] - (sum+=lt[i][k]*@x[k]) }
    end

    print_mat(@mat) { puts '所要求解的线性方程为:'}
    @n.times { |i| printf("x[%d]=%.3f \n", i+1, @x[i]); }
  end
end

class Order

  def initialize
    @mat = Array.new(4,[])
    @mat[1]=[0,1,2,3,1]
    @mat[2]=[0,5,4,10,0]
    @mat[3]=[0,3,-0.1,1,2]
    @n=3
  end

  def print_mat
    yield
    @n.times do | i |
      (@n+1).times do | j |
        printf("%.2f ",@mat[i+1][j+1])
      end
      puts ''
    end
  end
  private :print_mat

  def print_ans
    print "解为:"
    @n.times do | k |
      printf("%.2f ",@mat[k+1][@n+1])
    end
    printf "\n\n"
  end
  private :print_ans

  def calculate_answer
    @mat[@n][@n+1] /= @mat[@n][@n]
    (@n-1).downto(1) do |i|
      @mat[i][@n+1]-=(i+1..@n).reduce(0) {|sum,j| sum+=@mat[i][j]*@mat[j][@n+1]}
    end
  end
  private :calculate_answer

  def calculate_mat(i)
    (i+1).upto(@n+1) do | j |
      @mat[i][j]/=(1.0*@mat[i][i])
    end
    @mat[i][i]=1
    (i+1).upto(@n) do | k |
      (i+1).upto(@n+1) do | j |
        @mat[k][j]-=@mat[k][i]*@mat[i][j]
      end
    end
  end
  private :calculate_mat

  def original_order
    print_mat {puts "顺序消去法:\n原矩阵为:"}
    1.upto(@n-1) do | i |
      calculate_mat(i)
    end
    print_mat {puts '所得上三角矩阵为:'}
    calculate_answer
    print_ans
  end

  def col_order
    print_mat {puts "列主元素消去法:\n原矩阵为:"}
    1.upto(@n-1) do | i |
      now,maxx=i,@mat[i][i]
      (i+1).upto(@n) do | j |
        if(@mat[j][i]>maxx)
          maxx,now=@mat[j][i],j
        end
      end

      if i!=now
        (i..@n+1).each do | k |
         @mat[i][k],@mat[now][k] = @mat[now][k],@mat[i][k]
        end
      end
    calculate_mat(i)
    end
    print_mat {puts '所得上三角矩阵为:'}
    calculate_answer
    print_ans
  end

  def all_order
    print_mat {puts "全主元素消去法:\n原矩阵为:"}
    1.upto(@n-1) do | i |
      nx,ny,maxx=i,i,@mat[i][i]

      i.upto(@n) do | j |
        i.upto(@n) do | t |
          if(@mat[j][t]>maxx)
            maxx,nx,ny=@mat[j][t],j,t
          end
        end
      end

      if i!=nx #row exchange
        i.upto(@n+1) do | k |
          @mat[i][k],@mat[nx][k] = @mat[nx][k],@mat[i][k]
        end
      end

      if i!=ny # col exchange
        i.upto(@n) do | k |
          @mat[k][i],@mat[k][ny] = @mat[k][ny],@mat[k][i]
        end
      end

      calculate_mat(i)
    end
    print_mat {puts '所得上三角矩阵为:'}
    calculate_answer
    print_ans
  end

end

class VarStep
  def initialize
    @val = (1 + getVal(1))/2
    @pre,@n = 0x3f3f3f3f,1
  end

  def getVal(x)
    Math.sin(x) / x
  end

  def func(n,pre)
    sum,x = 0.0,0
    n.times do
      x = x + 1.0/(2*n)
      sum = sum + getVal(x)
      x = x + 1.0/(2*n)
    end
    @pre/2.0 + sum/(2*n)
  end

  def calculate_ans
    while (@val-@pre).abs > $eps
      @pre = @val
      @val = func(@n,@val)
      @n*=2
    end
    puts '积分函数为sin(x)/x,积分区间为[0,1]'
    printf("积分结果为%.8f\n",@val)
  end

end

class FourthRomberg
  def initialize
    @a,@b,@n=1,5,5
    @x,@y,@k=[],[],[]
    @h=1.0*(@b-@a)/@n
    @x[0],@y[0]=@a,17
  end

  def der_func(x,y)
    x-y+1
  end
  private :der_func

  def calculate_ans
    1.upto(@n) do |i|
      @x[i-1]=@x[0]+(i-1)*@h
      h1=@h/2.0
      @k[1]=der_func(@x[i-1],@y[i-1])
      @k[2]=der_func(@x[i-1]+h1,@y[i-1]+h1*@k[1])
      @k[3]=der_func(@x[i-1]+h1,@y[i-1]+h1*@k[2])
      @k[4]=der_func(@x[i-1]+@h,@y[i-1]+@h*@k[3])
      @y[i]=@y[i-1]+@h*(@k[1]+2*@k[2]+2*@k[3]+@k[4])/6.0
    end

    puts "a=1,b=5\nn=5\ny0=17\nf(x)=x-y+1\n"
    @n.times do |i|
      printf("y[%d]: %.2f\n",i+1,@y[i+1])
    end
  end
end

class ImproveEuler
  def initialize
    @up,@down,@n = 0.5,0,5
    @h = (@up-@down) / @n
    @xn = @down
    @yn = 1
  end

  def der_func(x,y)
    x - y + 1
  end

  def calculate_ans
    print "f(x,y)=x-y+1\n积分区间为[0,0.5]\ny0=1\n区间5等分\n"
    @n.times do | i |
      xl = @xn + @h
      ylb = @yn + @h * der_func(@xn,@yn)
      yl = @yn + @h / 2 * (der_func(@xn,@yn)+der_func(xl,ylb))
      printf("x%d=%f,y%d=%f\n", i, xl, i, yl)
      @xn,@yn = xl,yl
    end
  end
end

class Lagrance
  def initialize
    @n,@m,@ans=2,195,0
    @x=[121,144,100]
    @y=[11,12,10]
  end

  def calculate_ans
    0.upto(@n) do |i|
      key=1.0*@y[i]
      0.upto(@n) do |j|
        next if(i==j)
        key*=(1.0*(@m-@x[j])/(@x[i]-@x[j]))
      end
      @ans+=key
    end
    puts '给定3个坐标:'
    puts 'x[0]:121,y[0]:11'
    puts 'x[1]:144,y[1]:12'
    puts 'x[2]:100,y[2]:10'
    printf("询问195的y值为%.2f\n",@ans)
  end
end

class NewtonInterpolate
  def initialize
    @n=4
    @form=[0.0]*6
    @x=[10,9,3,11,2]
    @y=[100,80,9,123,4]
  end

  def calculate_ans
    @n.times { |i| @form[i]=@y[i] }
    (@n+1).times do |i|
      @n.downto(i+1) { |j| @form[j] = (@form[j]-@form[j - 1]) / (@x[j] - @x[j - 1 - i]) }
    end
    tmp,hx=1,6.42
    newton=@form[0]
    @n.times do |i|
      tmp*=(hx-@x[i])
      newton+=tmp*@form[i+1]
    end
    @n.times { |i| printf("第%d个点%.2f %.2f\n",i+1,@x[i],@y[i]) }
    printf("F(%.2f) = %.2f\n", hx, newton)
  end
end

class LeastSquare
  def initialize
    @x=[10,9,5,6,2]
    @y=[99,81,24,37,4]
    @a,@b=0.0,0.0
    @n=5
  end

  def get_val(x)
    @a*x+@b
  end

  def calculate_ans
    t=[0.0]*4
    @n.times do |i|
      t[0]+=@x[i]*@x[i]
      t[1]+=@x[i]
      t[2]+=@x[i]*@y[i]
      t[3]+=@y[i]
    end
    @a=(t[2]*@n-t[1]*t[3])/(t[0]*@n-t[1]*t[1])
    @b=(t[0]*t[3]-t[1]*t[2])/(t[0]*@n-t[1]*t[1])
    printf("所得函数为%dx%d\n",@a,@b)
    puts '测试数:12'
    puts get_val(12)
  end
end

class Romberg
  @@pi=4.0*Math.atan(1.0)
  def initialize
    @a,@b=0.000001,1.0
    @n,@h=20,Float(@b-@a)
    @t=20.times.map{ [0] * 2 }
    @t[0][1]=trapezium
    @n*=2
  end

  def get_val(x)
    Math.sin(x)/x
  end
  private :get_val

  def trapezium
    @h=(@b-@a)/@n
    sum = 1.upto(@n-1).reduce(0.0) {|tmp,i| tmp+=get_val(@a+i*@h) }
    sum+=(get_val(@a)+get_val(@b))/2.0
    @h*sum
  end

  def calculate_ans

    puts 'sin(x)/x在[0,1]区间内的积分测试:'
    (1..9).each do |m|
      m.times { |i| @t[i][0]=@t[i][1] }
      @t[0][1]=trapezium
      @n*=2
      m.times do |i|
        @t[i+1][1]=@t[i][1]+(@t[i][1]-@t[i][0]) / ((4**m)-1)
      end
      if (@t[m-1][1]-@t[m][1]).abs < $eps
        printf("计算的数为:%.6f\n", @t[m][1])
        return @t[m][1]
      end
    end
    puts 'No Answer'
  end
end

def nodekey
  while true
    puts '<===================================>'
    puts '1.二分法求解二元一次方程的根'
    puts '2.牛顿法求解一元多次方程的根'
    puts '3.顺序去法解线性方程组'
    puts '4.列选主元消去法解线性方程组'
    puts '5.全选主元消去法解线性方程组'
    puts '6.Doolittle分解线性方程组'
    puts '7.Crout分解线性方程组'
    puts '8.平方根法求线性方程组'
    puts '9.拉格朗日插值法'
    puts '10.牛顿插值法'
    puts '11.最小二乘法——线性拟合'
    puts '12.变步长梯形求积算法计算积分'
    puts '13.龙贝格算法计算积分'
    puts '14.改进欧拉算法求常微分方程的数值解'
    puts '15.四阶龙格-库塔法求常微分方程的数值解'
    puts ' 0.退出'
    puts '<====================================>'
    print "请选择你要选择的操作:"
    op=gets.to_i
    case op
      when 0
        break
      when 1
        TwoSplit.new.calculate_ans
      when 2
        NewtonIterate.new.calculate_ans
      when 3
        Order.new.original_order
      when 4
        Order.new.col_order
      when 5
        Order.new.all_order
      when 6
        ResolveMat.new.doolittle
      when 7
        ResolveMat.new.crout
      when 8
        ResolveMat.new.square
      when 9
        Lagrance.new.calculate_ans
      when 10
        NewtonInterpolate.new.calculate_ans
      when 11
        LeastSquare.new.calculate_ans
      when 12
        VarStep.new.calculate_ans
      when 13
        Romberg.new.calculate_ans
      when 14
        ImproveEuler.new.calculate_ans
      when 15
        FourthRomberg.new.calculate_ans
      else
        puts '请输入0-15的选项,选择0为退出'
    end
  end
end

nodekey
Ruby

以Ruby为例浅谈动态语言的赋值机制

Posted on
  1. 问题提出
  2. 结论
  3. 数组解释
  4. 数字解释 1
  5. 数字解释 2
  6. 对该特性的思考
  7. Ruby与Python的内存管理机制
1.问题提出

最近的数值计算实验纠结了一下最后想用Ruby解决,结果在中途发现了这样的一个问题。

定义一个二维数组ary,我先是给 ary[0][0]赋值为 1,再是给 ary[1][0]赋值为 233,结果输出 ary[0][0] 居然为 233,输出整个ary ,发现每个数组第一项都是 233 。

详细介绍的可以看我在Ruby China发的帖子

2.结论

在这里我以Ruby为例详细介绍一下动态语言的赋值机制。

首先我们先明确几点:
1. 在ruby中参数的传递永远都是引用。
2. 每一个变量保存的都是对对象的引用。
3. 我们访问对象都是通过引用间接访问的。

3.数组解释

这该如何理解呢?以我上面那个Array.new(6,Array.new(6,0))来说,ruby先执行里表达式,创建一个Array对象并返回其引用 设为α;然后执行,外Array.new,创建6个对 α 的引用,就是对引用的引用,但最后还是引用自对象。因此ary的6个对象都为引用,如果我改变这个对象的值,那么ary的6个引用都将随之改变。

那么如果我要定义一个数组,并初始化其中几个值又应该怎么做呢。

当然是去从本质上解决问题啊。答案应该是将后一个Array.new通过块传入:

Arrar.new(6) { Array.new(6,0) }

假设你拥有block的知识,在 6 次Array.new 的每个块中,都会执行一次 Array.new(6,0)并返回这个Array.new的引用,就会得到6个不相同的对象。简单说,通过block传参,block中的代码每次都会被重新执行一遍。而直接传参就只会将参数的引用传递过来。

4.数字解释 1

解决了数组问题,再来解释一下最平常不过的数字。先贴上我的理解来源,并向大佬表示感谢。

def pref3(a)
  a = 5
  a.object_id
end

a = 1
a.object_id #输出3

pref3(a) #输出11
puts a 

这段代码很容易陷入怀疑 在ruby中参数的传递永远都是引用 正确性的误区:
“如果我传递的是引用,那么我在函数内部发生的变化理应对外部也产生影响呀,为什么输出仍然是1呢??”

在这里,经过 pref3 的调用, a仍然会是 3 ,然而在函数内和函数外的 object_id 却是不同的。这很显然,我们会很自然地去认为,这两个 a 所引用的并不是同一个对象,而实际上也是如此。

现在让我们来重新理解一下Ruby的赋值机制。它大致分为两个步骤:
1. 根据右值创建对象并返回该对象的引用。
2. 不论左值是否为空,左值所引用的对象又是什么类型,右值的引用都会将其覆盖。

也就是说在函数体 pref3 中的 a 初始是 ==对象 1== 的引用,而在执行 a = 5 时,ruby会先创建一个==对象 5== , 并使其引用覆盖掉 a 的原引用对象 ==对象 1==。
至此,一切都理论都得以成立。

5.数字解释 2

再试着来理解一下这段代码

def pref3(a,b)
  a,b = b,a
end

a,b = 1,2
pref3(a,b)
puts a,b #输出 1,2

根据上述,每一个变量保存的都是对 对象的引用,那么在 pref3 中,a,b,其实是对函数外 a,b的引用,即==对象 1和对象 2==引用的引用,通过递归仍然是对==对象 1和对象 2==的引用。
而在此我交换 a,b 实质上是把,b的对象引用覆盖给了a,a的对象引用覆盖给了b,而函数中的两个变量 a,b ,因为其本身是局部变量,且为引用,引用之间的交换并不会对函数外的对象引用产生任何影响,更不会对所引用对象产生任何影响。

于是乎,结果仍然是

1,2

6. 对该特性的思考

总的来说,这个赋值机制还是比较方便的,比较纯粹的。我本人就比较喜欢纯粹的事物,Linux下一切皆文件,Ruby下一切皆对象,动态语言下一切变量皆引用,一切问题皆网络流!!

但是,在我突然意识到,如果我的理论一切都完美成立的话,就会发生内存上的问题。因为我们在创建对象的时候返回的是其引用,我们根本无法直接访问到它,更别谈什么主动释放了。

为什么我想主动释放呢,也许我们对一些特殊的变量一申请,一创建,就会从头用到尾,但是Ruby下一切皆对象,对于数字和字符串这些频繁使用的对象来说,不释放空间非常容易产生内存问题。

然而睡了个觉,回头一想,突然想起了Programming Ruby中关于内存释放有一个自己的机制:
ruby在每次创建变量,传递参数的时候都会对被引用次数进行计数,如果这个被引用次数变为0了,就会自动被释放。这个机制实在是妙哉!!

一想到这,我想我跟松本弘行应该是想到一块去了。蛤蛤蛤!


updated time : May 16

6. Ruby与Python的内存管理机制

自己YY就以为这个问题解决了,没想到被大佬指出了错误,真的是醍醐灌顶。

引用计数这种内存管理机制是地的确确存在的,并且是python所使用的,但Ruby用的是另外一种管理机制 mark and sweep。
首先献给处大佬给我学习的 博文 ,并对大佬和作者表示感谢,然而这篇文章,主要针对的还是小白,如果用来复习就有点得不偿失了。所以我这里简单说一下个人理解,如果与原博文有任何冲突,一切以原博文为主。

Ruby:
预先创建一个空闲链表,一旦有 new 申请就直接从里面拿取空间,如果链表空间不够,则停止其他所有程序活动,不断迭代所有对象的引用,对 所得到的链表内部对象 进行标记,最后遍历链表,清理未标记内存,将其重新构建成一个新的空闲链表。
更深的,基于弱代假说:

年亲的对象通常死得快,而老对象则很有可能存活更长的时间。

对一次 sweep 后被标记的对象,就会被Ruby划分为成熟对象,并在之后很长一段时间内,打赌他们不会再被取消标记。也就是说这些对象 ruby 在很长一段时间内将不会去遍历判断。
而当成熟对象的数目双倍于上次全局回收的数目时,Ruby会清理所有的标记并将所有的对象都视为新对象以重新清理。

Python:
python采取引用标记机制,因为互相引用的情况存在,python不得不用上了类似与ruby的方法,建立一个引用链表称为0代链表,如果内存空间达到阈值就对链表内部进行 对循环引用的检测。其中,存留下来的转移至 1代链表 ,同理会转移至 2代链表(最多2代)。虽然都是转移,但是阈值显然是不同的,对于 0 代链表的操作显然要活跃一些,所以阈值应该是要低一些(猜测,原文中并没有说……)。然而对 2代链表之后的事情我就不清楚了……

然而说到底,这些都是博主说的,是否正确我也无法证实,但是看上去貌似灰常有道理。如果要深入研究,到头来还是得看源码……