我正在尝试使用 Ruby 来模拟 DrRacket 中的
append
函数。然而,当我尝试时,我得到的是((1 2 3) . 99)
而不是(1 2 3 . 99)
。我相信这与我的 to_s
方法有关,因此我尝试在附加新值之前先创建列表的新副本。但我不知道该怎么办。请尽快指教。
#Project 4
#Kevin Iwatsuki
#Description: This program is meant to help the user become familiar with the basics of Ruby by duplicating the Racket programming language's pairing mechanisms (cons, car, cdr, etc.).
##########
#Pair (class): Takes two values and creates a pair from them in the style of the Racket programming language. Executing the code should return a pair or list without displaying any output.
# Returns: A pair or list.
# Parameters:
# value1 (any class, including pair) - a value to insert into list.
# value2 (any class, including pair) - another value to insert into list.
#
#Declare class `Pair`
class Pair
#initialize: Constructor that enables the creation of Pair objects. This one declares that the objects must have two parameters.
# Returns: A new Pair class object.
# Parameters:
# value1 (any class, including pair) - a value to insert into list.
# value2 (any class, including pair) - another value to insert into list.
def initialize(value1, value2)
@value1 = value1
@value2 = value2
end
#car: return the first value of the pair.
# Returns: The first value of a pair.
# Parameters: none
def car
return @value1
end
#cdr: return the values after the first value of the pair.
# Returns: The the values after the first value of the pair.
# Parameters: none
def cdr
return @value2
end
#to_s: Uses helper_to_s to return a string representation of the pair or list.
# Returns: A string of the pair/list.
# Parameters: none
def to_s
return "(#{helper_to_s}"
end
#helper_to_s: A method that uses recursion to return the appropriate string objects for the pair/list. Meant to be used by the `to_s` method.
# Returns: A string of the pair/list to be used with to_s.
# Parameters: none
def helper_to_s
#Base case: If value1 and value2 are not Pair class objects and value2 is not a nil value, then return a string representing a pair.
if(@value1.class != Pair && @value2.class != Pair && @value2 != nil)
return "#{@value1} . #{@value2})"
#Recursive Case: If value1 is a Pair object and value2 is not a Pair and also not a nil value, submit value1 to `helper_to_s`` for recursion and print the results as a string along with the cdr. The returned value will be in pair form.
elsif(@value1.class == Pair && @value2.class != Pair && @value2 != nil)
return "(#{car.helper_to_s} . #{cdr})"
#Base case: If value2 is a nil value, then return a string representing a list.
elsif(@value2 == nil)
return "#{@value1})"
#Recursive case: If the second value is a Pair object, recursively call the helper function using the cdr until a base case is reached.
elsif(@value2.class == Pair)
return "#{@value1} #{cdr.helper_to_s}"
end
end
#list?: One of two list? methods. Checks if a pair/cons is a list.
# Returns: true if pair is a valid list and false otherwise.
# Parameters: none
def list?
#If value2 is a pair...
if (@value2.class == Pair)
#...Recursively call the list? method again with the cdr method.
cdr.list? #Recursion
else
#If value2 is a null (nil) value, then it is a list.
if (@value2 == nil)
return true
else
return false
end
end
end
#Method: append(other) - If the pair is a list, append should return a new list consisting of `other`` appended to the original list.
def append(other)
#First, we must check if the Pair object to append to is a list.
if(!list?)
# Return false if not a list.
return false
#If Pair object to append to is a list, do the following:
else
return "(#{helper_append(self)} . #{other})"
end
end
#Helper method for append method.
def helper_append(lst)
return lst.car
end
#self.null: Define a null method in Pair class so `Pair.null` can be used.
# Returns: nil value
# Parameters: none
def self.null
nil
end
end
#Opens Ruby's NilClass for the sake of adding methods applicable for null values.
class NilClass
#list?: One of two list? methods. When checking if a `Pair.null` is a list, we must create a `list?` method for the preexisting NilClass in Ruby.
# Returns: true if only `Pair.null`` is being checked.
# Parameters: none
def list?
#A null list is always a list.
return true
end
#Return an empty list if a Pair.null is found.
def to_s
return "()"
end
end
#cons: A global method that creates a new Pair class object whenever invoked.
# Returns: A new Pair object.
# Parameters:
# val1 (any type, including pair) - a value to insert into cons.
# val2 (any type, including pair) - another value to insert into cons.
def cons(val1, val2)
Pair.new(val1, val2)
end
#1.)
a = Pair.new(5, 7)
puts a.append(99) #should return false
# #2.)
b = cons(1, cons(2, cons(3, Pair.null)))
puts b.append(99) #Should return (1 2 3 . 99)
您的问题有两点:
append
基本上类似于 cons
,从某种意义上说,它还应该向底层 list 添加项目。然而在这个例子中,它实际上返回一个字符串,而不改变 Pair
对象的状态。
您实际需要做的是将
append
新项目添加到 pair/list
结构的末尾。为了实现这一点,您需要遍历这些对(假设它是一个有效的列表),直到找到最后一项,它应该是null
。
def append(other)
return false unless list?
if cdr.nil?
@value2 = other
else
cdr.append(other)
end
self
end
如果您想要
self
内联(如您的示例中所示),则使用 puts
返回“现有”对象也很重要。
以下面的代码为例:
b = cons(1, Pair.null)
c = cons(4, Pair.null)
puts b.append(c)
append
这里将确保创建以下结构:
<Pair @value1=1, @value2=<Pair @value1=4, @value2=nil>>
list
现在,为了打印列表,这又是一个递归问题(就像第一个问题一样)。您需要浏览配对列表,但也要考虑不同的情况。这就是为什么保持有效的数据结构如此重要。
def to_s
helper_to_s(true)
end
def helper_to_s(started = false)
str = ""
str += "(" if started
str += "(" if started && car.is_a?(Pair)
if car.is_a?(Pair)
str += car.helper_to_s
else
str += car.to_s
end
if cdr.nil?
str += ")"
elsif cdr.is_a?(Pair)
str += " "
str += cdr.helper_to_s()
else
str += " . #{cdr})"
end
str
end
我们需要一个
helper function
,以便我们可以告诉递归调用何时 open
(
。此外,我们还需要立即考虑嵌套对的情况。最后,由于我们的数据结构,我们可以判断 cdr 何时不是 null
,这意味着它不是 pure list
,在这种情况下,我们可以简单地将 .
连接到最终字符串,它将在to_s
致电。
如果您还有其他问题,请务必查看球拍文档中的配对和列表文档:https://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html这真的很有帮助。