Class: R::List

Inherits:
Object show all
Includes:
Enumerable, IndexedObject
Defined in:
lib/R_interface/rlist.rb

Overview



Constant Summary

Constants inherited from Object

Object::EXPLICIT_RUBY_SURFACE

Instance Attribute Summary

Attributes inherited from Object

#expression, #r_interop

Instance Method Summary collapse

Methods included from IndexedObject

#[], #[]=, #size

Methods inherited from Object

#==, build, build_class_probe_count, build_from_r_class_string, build_from_wrapper_tag, #call, class_probe_fetch_r_class, increment_build_class_probe_count!, #initialize, #inspect, #instance_of?, #instance_variable_get, #instance_variable_set, #is_a?, #method_missing, #nil?, #pretty_print, #rclass, reset_build_counters!, #respond_to?, #send, #to_ary, #to_i, #to_ruby, #to_s, #typeof

Methods included from ExecUniOp

#exec_uni_oper

Methods included from UnaryOperators

#!, #+@, #-@

Methods included from ExecBinOp

#coerce, #exec_bin_oper

Methods included from BinaryOperators

#!=, #%, #*, #**, #-, #/, #<, #<=, #>, #>=, #_, #eq, #int_div, #til

Constructor Details

This class inherits a constructor from R::Object

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class R::Object

Instance Method Details

#+(other_object) ⇒ Object





42
43
44
# File 'lib/R_interface/rlist.rb', line 42

def +(other_object)
  ::R::Support.exec_function_name("`+`", @r_interop, other_object.r_interop)
end

#>>(index) ⇒ Object



149
150
151
152
153
# File 'lib/R_interface/rlist.rb', line 149

def >>(index)
  # Count the list root as depth 1 so MAX_UNBOX_DEPTH applies to list nesting
  # the same way legacy unboxing specs expect.
  unboxed_get(index, 1)
end

#classObject



34
35
36
# File 'lib/R_interface/rlist.rb', line 34

def class
  ::R::List
end

#eachObject


Each cannot return a Enumerator because R is single threaded. When this restriction is removed, make each return self.to_enum



160
161
162
163
164
165
166
167
168
169
# File 'lib/R_interface/rlist.rb', line 160

def each

  len = length
  len = len.is_a?(::R::Vector) ? len.unboxed_get(0) : len
  (1..len).each do |i|
    # In Ruby list[[i]] is list.[]( [i] ) → single Array arg → we use R's [[ (element)
    yield self[[ [i] ]]
  end

end

#each_with_indexObject


Need to override each_with_index, as R indexing starts at 1



175
176
177
178
179
180
181
182
183
# File 'lib/R_interface/rlist.rb', line 175

def each_with_index

  len = length
  len = len.is_a?(::R::Vector) ? len.unboxed_get(0) : len
  (1..len).each do |i|
    yield self[[ [i] ]], i
  end

end

#method_missing_assign(elmt_name, arg) ⇒ Object





50
51
52
# File 'lib/R_interface/rlist.rb', line 50

def method_missing_assign(elmt_name, arg)
  setR_name("`[[<-`", elmt_name, arg)
end

#unboxed_get(index = nil, depth = 0) ⇒ Object


Unbox list to Ruby. Recurses until only Ruby values (no R::Object in result).

  • index.nil? → whole list → Array of unboxed elements (single list → [x]).
  • index i → list[[i+1]] unboxed (one Ruby value). Raises R::UnboxDepthError when recursion exceeds MAX_UNBOX_DEPTH (can be expensive).



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/R_interface/rlist.rb', line 60

def unboxed_get(index = nil, depth = 0)
  if depth >= ::R::Support::MAX_UNBOX_DEPTH
    ::Kernel.raise(::R::UnboxDepthError, "unbox: list too deep (max depth #{::R::Support::MAX_UNBOX_DEPTH} exceeded)")
  end
  if index.nil? && depth <= 1 && ::R.bridge.respond_to?(:unbox_materialize)
    materialized = ::R.bridge.unbox_materialize(@r_interop,
                                                max_depth: ::R::Support::MAX_UNBOX_DEPTH,
                                                max_nodes: 200_000)
    case materialized[:status]
    when :ok
      return materialized[:value]
    when :depth_limit
      ::Kernel.raise(::R::UnboxDepthError, "unbox: list too deep (max depth #{::R::Support::MAX_UNBOX_DEPTH} exceeded)")
    when :node_limit
      # Fall through to structural path if node budget is hit.
    when :unsupported
      # Fall through to structural path for mixed/unsupported types.
    end
  end
  # Fast preflight for deep nested lists on the new bridge:
  # detect depth overflow in one bridge call before recursive element reads.
  if index.nil? && depth <= 1 && ::R.bridge.respond_to?(:unbox_walk)
    walk = ::R.bridge.unbox_walk(@r_interop,
                                 max_depth: ::R::Support::MAX_UNBOX_DEPTH,
                                 max_nodes: 200_000)
    if walk[:status] == :depth_limit
      ::Kernel.raise(::R::UnboxDepthError, "unbox: list too deep (max depth #{::R::Support::MAX_UNBOX_DEPTH} exceeded)")
    end
  end
  # NewBridge hardening path: use structural bridge reads during recursion.
  if ::R.bridge.respond_to?(:unbox_list_length) && ::R.bridge.respond_to?(:unbox_list_element)
    n = ::R.bridge.unbox_list_length(@r_interop)
    if index.nil?
      return [] if n <= 0
      arr = []
      (1..n).each do |i|
        elt = ::R.bridge.unbox_list_element(@r_interop, i)
        if elt.nil?
          arr << nil
        elsif !elt.is_a?(::R::Object)
          arr << elt
        else
          arr << elt.unboxed_get(nil, depth + 1)
        end
      end
      return arr
    end

    bound = n - 1
    ::Kernel.raise(::IndexError.new("index #{index} out of list bounds: 0...#{bound}")) if index > bound
    elt = ::R.bridge.unbox_list_element(@r_interop, index + 1)
    return elt unless elt.is_a?(::R::Object)
    return nil if elt.nil?
    return nil if elt.respond_to?(:rclass) && elt.rclass.to_s.strip == 'NULL'
    return elt.unboxed_get(nil, depth + 1)
  end

  if index.nil?
    # Whole list → Array of unboxed elements (recurse into each element).
    len_val = length
    n = len_val.is_a?(::R::Vector) ? len_val.unboxed_get(0) : len_val
    n = n.to_i if n.respond_to?(:to_i)
    return [] unless n.is_a?(::Integer) && n >= 1
    arr = []
    (1..n).each do |i|
      elt = self[[ [i] ]]
      if elt.nil?
        arr << nil
      elsif !elt.is_a?(::R::Object)
        arr << elt
      else
        arr << (elt.is__null.unboxed_get(0) ? nil : elt.unboxed_get(nil, depth + 1))
      end
    end
    return arr
  end

  # Single element at index
  bound_val = length
  bound = bound_val.is_a?(::R::Vector) ? bound_val.unboxed_get(0) : bound_val
  bound = bound.to_i - 1 if bound.respond_to?(:to_i)
  bound = 0 if bound.nil? || !bound.is_a?(::Integer) || bound < 0
  ::Kernel.raise(::IndexError.new("index #{index} out of list bounds: 0...#{bound}")) if index > bound
  elt = self[[ [index + 1] ]]
  return elt unless elt.is_a?(::R::Object)
  return nil if (elt.is__null.respond_to?(:unboxed_get) ? elt.is__null.unboxed_get(0) : elt.is__null)
  elt.unboxed_get(nil, depth + 1)
end