使用Ruby模拟DrRacket的append函数遇格式异常求助
问题分析与解决
首先得明确DrRacket里append的行为:当第二个参数不是列表时,它会把第一个列表的最后一个cons单元的cdr替换成第二个参数,生成一个非严格列表(improper list)。比如(append '(1 2 3) 99)的结果就是(1 2 3 . 99)。
你现在得到((1 2 3) . 99),说明你可能直接把整个第一个列表作为新cons对的car,而没有遍历到它的末尾去修改cdr——相当于错误地嵌套了原列表,而非延续它的结构。
能不能直接用已有列表模拟?
可以,但会有副作用:
- 如果你的列表是用可变结构(比如自定义的
Cons类,每个实例有可修改的car和cdr属性)实现的,你可以遍历到原列表的最后一个cons单元,把它的cdr改成第二个参数。这样得到的结果就是预期的(1 2 3 . 99),但原列表会被永久修改——原来的'(1 2 3)会变成'(1 2 3 . 99),如果其他地方还在引用这个列表,会受到影响。
要不要复制列表?
如果不想破坏原列表,就必须复制:
- 遍历原列表,逐个复制每个cons单元,直到最后一个,然后把复制出来的最后一个cons的
cdr设为第二个参数。这样原列表保持不变,新生成的就是你要的非严格列表。
示例实现(基于自定义Cons类)
直接修改原列表(有副作用)
class Cons attr_accessor :car, :cdr def initialize(car, cdr) @car = car @cdr = cdr end def to_s return "#{car}" if cdr.nil? return "(#{car} . #{cdr})" unless cdr.is_a?(Cons) rest = cdr.to_s[1..-2] "(#{car} #{rest})" end end def append!(list1, list2) current = list1 current = current.cdr while current.cdr.is_a?(Cons) current.cdr = list2 list1 end # 测试 list = Cons.new(1, Cons.new(2, Cons.new(3, nil))) puts append!(list, 99) # 输出 (1 2 3 . 99) puts list # 原列表已被修改,同样输出 (1 2 3 . 99)
复制列表(无副作用)
def append(list1, list2) return list2 if list1.nil? Cons.new(list1.car, append(list1.cdr, list2)) end # 测试 original_list = Cons.new(1, Cons.new(2, Cons.new(3, nil))) new_list = append(original_list, 99) puts new_list # 输出 (1 2 3 . 99) puts original_list # 原列表未被修改,输出 (1 2 3)
总结:如果不在乎原列表被修改,直接修改末尾的cdr即可;如果需要保留原列表的完整性,必须复制列表结构。
内容的提问来源于stack exchange,提问作者spaceman777
相关产品推荐
相关产品推荐

