Add RecordSeries::get_record and support for (my_ticks 0)
This commit introduces the `get_record` method to `RecordSeries`, allowing for the dynamic reconstruction of a full record from its constituent fields at a given index. This enables idiomatic access to series data using indexing like `(my_ticks 0)`. The `push-series` function has also been updated to correctly handle the structure of records when pushing data into a `RecordSeries`.
This commit is contained in:
@@ -2,12 +2,12 @@
|
||||
;; Benchmark: 998ns
|
||||
;; Benchmark-Repeat: 2047
|
||||
(do
|
||||
(def template {:price 10.0 :volume 100})
|
||||
(def template {:price 10.0 :volume 100 :msg "hi"})
|
||||
(def my_ticks (create-series template))
|
||||
|
||||
(push-series my_ticks {:price 10.5 :volume 100})
|
||||
(push-series my_ticks {:price 11.2 :volume 200})
|
||||
(push-series my_ticks {:price 15.5 :volume 300})
|
||||
(push-series my_ticks {:price 10.5 :volume 100 :msg "A"})
|
||||
(push-series my_ticks {:price 11.2 :volume 200 :msg "B"})
|
||||
(push-series my_ticks {:price 15.5 :volume 300 :msg "C"})
|
||||
|
||||
(def prices (.price my_ticks))
|
||||
|
||||
|
||||
+26
-3
@@ -251,6 +251,25 @@ impl RecordSeries {
|
||||
pub fn field(&self, key: Keyword) -> Option<Rc<std::cell::RefCell<dyn SeriesMember>>> {
|
||||
self.layout.index_of(key).map(|idx| self.fields[idx].clone())
|
||||
}
|
||||
|
||||
/// Dynamically reconstructs the full Record at the given lookback index.
|
||||
/// This is an O(N) operation where N is the number of fields, meaning it breaks
|
||||
/// the 0-Copy performance path, but it's essential for idiomatic AST access `(series 0)`.
|
||||
pub fn get_record(&self, index: usize) -> Option<Value> {
|
||||
if self.fields.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut vals = Vec::with_capacity(self.fields.len());
|
||||
for field in &self.fields {
|
||||
match field.borrow().get_value(index) {
|
||||
Some(v) => vals.push(v),
|
||||
None => return None, // Out of bounds or incomplete
|
||||
}
|
||||
}
|
||||
|
||||
Some(Value::Record(self.layout.clone(), Rc::new(vals)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RecordSeries {
|
||||
@@ -356,8 +375,13 @@ pub fn register(env: &Environment) {
|
||||
}
|
||||
let (series_val, record_val) = (&args[0], &args[1]);
|
||||
|
||||
if let Value::Object(obj) = series_val {
|
||||
if let Some(record_series) = obj.as_any().downcast_ref::<RecordSeries>() {
|
||||
let record_series = if let Value::Object(obj) = series_val {
|
||||
obj.as_any().downcast_ref::<RecordSeries>()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(record_series) = record_series {
|
||||
if let Value::Record(_, values) = record_val {
|
||||
// Push each value into its corresponding column (SoA push)
|
||||
for (i, field_val) in values.iter().enumerate() {
|
||||
@@ -370,7 +394,6 @@ pub fn register(env: &Environment) {
|
||||
panic!("push-series expects a record as the second argument");
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("push-series expects a RecordSeries as the first argument")
|
||||
},
|
||||
);
|
||||
|
||||
@@ -547,6 +547,24 @@ impl VM {
|
||||
} else {
|
||||
break Err("Series index must be an integer".to_string());
|
||||
}
|
||||
} else if let Some(rs) = obj.as_any().downcast_ref::<crate::ast::rtl::series::RecordSeries>() {
|
||||
// Support for `(my_ticks 0)` - reconstructs the full Record from SoA!
|
||||
if arg_vals.len() != 1 {
|
||||
break Err("RecordSeries indexer expects exactly 1 argument (the lookback index)".to_string());
|
||||
}
|
||||
if let Value::Int(idx) = arg_vals[0] {
|
||||
if idx < 0 {
|
||||
break Err("RecordSeries lookback index cannot be negative".to_string());
|
||||
}
|
||||
if let Some(rec) = rs.get_record(idx as usize) {
|
||||
break Ok(rec);
|
||||
} else {
|
||||
// Out of bounds / not enough data yet
|
||||
break Ok(Value::Void);
|
||||
}
|
||||
} else {
|
||||
break Err("RecordSeries index must be an integer".to_string());
|
||||
}
|
||||
} else {
|
||||
break Err(format!("Object is not callable: {}", obj.type_name()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user