ruby つくって学ぶプログラミング言语Ruby中的方案

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ruby つくって学ぶプログラミング言语Ruby中的方案相关的知识,希望对你有一定的参考价值。

# 式expを評価するときに環境envが追加された。
def _eval(exp, env)
  # リストじゃなかったら
  if not list?(exp)
    # 数字の場合、そのまま返す。
    if immediate_val?(exp)
      exp
    # 変数だった場合は、環境から値を取り出す。
    else
      lookup_var(exp, env)
    end
  else
    # let式 λ式 の場合にマッチ
    if special_form?(exp)
      eval_special_form(exp, env)
    else
      # リストの先頭が関数, 残りが引数, それらを取り出したら適用
      fun = _eval(car(exp), env)
      args = eval_list(cdr(exp), env)
      apply(fun, args)
    end
  end
end

def special_form?(exp)
  lambda?(exp) or
    let?(exp)
end

def lambda?(exp)
  exp[0] == :lambda
end

def eval_special_form(exp, env)
  if lambda?(exp)
    # クロージャを作る。
    eval_lambda(exp, env)
  elsif let?(exp)
    # let式 -> λ式へと変換してから式をexp評価。
    eval_let(exp, env)
  end
end

def eval_list(exp, env)
  exp.map{|e| _eval(e, env)}
end

def list?(exp)
  exp.is_a?(Array)
end

# プリミティブ関数を内包したハッシュ。
$primitive_fun_env = {
  :+ => [:prim, lambda{|x, y| x + y}],
  :- => [:prim, lambda{|x, y| x - y}],
  :* => [:prim, lambda{|x, y| x * y}]
}

# リストの先頭
def car(list)
  list[0]
end

# 先頭の次から最後の要素までのリスト
def cdr(list)
  list[1..-1]
end

def immediate_val?(exp)
  num?(exp)
end

def num?(exp)
  exp.is_a?(Numeric)
end

def apply(fun, args)
  if primitive_fun?(fun)
    apply_primitive_fun(fun, args)
  else
    lambda_apply(fun, args)
  end
end

def primitive_fun?(exp)
  exp[0] == :prim
end

# プリミティブ関数に引数を適用する。
def apply_primitive_fun(fun, args)
  # プリミティブ関数から式bodyを取り出す。
  fun_val = fun[1]
  # 配列を引数に渡すときは、可変長にする。
  fun_val.call(*args)
end

# 環境env(ハッシュのリスト)から指定した変数の値を取り出す。
def lookup_var(var, env)
  # envからシンボルに対応するハッシュを探す。[Hash], :var -> Hash?
  var_hash = env.find{|vh| vh.key?(var)}
  if var_hash == nil
    raise "clodn't find value to variables:'#{var}'"
  end
  # Hash -> args
  var_hash[var]
end

# シンボルと値のリストから作られたハッシュを環境envへ追加する。
def extend_env(parameters, args, env)
  # シンボルと値の組のリスト
  var_hash = parameters.zip(args)
  # envへ追加するためにハッシュにする。
  h = Hash.new
  var_hash.each{|k,v| h[k] = v}
  [h] + env
end

# let式をλ式へ変換してから評価する。
def eval_let(exp, env)
  # 式から仮引数、実引数、評価する式を取り出す。
  parameters, args, body = let_to_parameters_args_body(exp)
  # λ式の形へ変換
  new_exp = [[:lambda, parameters, body]] + args
  _eval(new_exp, env)
end

# シンボル:letを除いた式の本体を機械的に取り出す。
def let_to_parameters_args_body(exp)
  [exp[1].map{|e| e[0]}, exp[1].map{|e| e[1]}, exp[2]]
end

def let?(exp)
  exp[0] == :let
end

def eval_lambda(exp, env)
  make_closure(exp, env)
end

# クロージャ(式 + 環境)を作る。
def make_closure(exp, env)
  parameters, body = exp[1], exp[2]
  [:closure, parameters, body, env]
end

# ラムダ式(というよりもクロージャ)を評価する。
def lambda_apply(closure, args)
  parameters, body, env = closure_to_parameters_body_env(closure)
  # 環境に新しい引数の列(仮引数+値の組)を取り入れて、新しい環境を作り出す。
  new_env = extend_env(parameters, args, env)
  # 式本体bodyを新しい環境で評価。
  _eval(body, new_env)
end

def closure_to_parameters_body_env(closure)
  [closure[1], closure[2], closure[3]]
end

$global_env = [$primitive_fun_env]
#exp = [[:lambda, [:x, :y], [:+, :x, :y]], 3, 2]
exp = [:let, [[:x, 3]],
 [:let, [[:fun, [:lambda, [:y], [:+, :x, :y]]]],
   [:+, [:fun, 1], [:fun, 2]]]]
puts _eval(exp, $global_env)

以上是关于ruby つくって学ぶプログラミング言语Ruby中的方案的主要内容,如果未能解决你的问题,请参考以下文章

ruby つくって学ぶプログラミング言语Ruby中的方案

题解 AT25 プログラミングコンテスト

AtCoder全国統一プログラミング王決定戦予選/NIKKEI Programming Contest 2019

挑战程序设计竞赛 PDF下载

ruby PCからTHETAのシャッターを切る最小限のサンプルプログラム

ruby 自回答の転记Ref:[Ruby - Rubyで次の仕様を満たすPokemonクラスを持つプログラム...(98411)| teratail](https://teratail.com/q