Skip to content

Commit e709127

Browse files
committed
Added an O(n) search linked-list persistent map tide me over (well,
tide me over, and surely useable for small data sets; this is, after all, how many-a-elisp project handles it, with plain alists
1 parent cd4c050 commit e709127

File tree

10 files changed

+524
-60
lines changed

10 files changed

+524
-60
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ dyn-clone = "*"
1111
pest = "*"
1212
pest_derive = "*"
1313
nom = "*"
14-
text_io = "*"
14+
text_io = "*"
15+
rand = "*"

src/ifn.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@
1111
//! ({:name "Blah" :age 20} :name)
1212
//! As well as a few more types.
1313
use crate::value::Value;
14+
1415
use dyn_clone::DynClone;
16+
1517
use std::fmt::Debug;
18+
use std::hash::Hash;
1619

1720
//
1821
// Based on: clojure.lang.IFn

src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ mod lambda;
1212
mod reader;
1313
mod persistent_list;
1414
mod persistent_vector;
15+
mod persistent_list_map;
1516
mod repl;
17+
mod maps;
1618

1719
use environment::Environment;
1820

@@ -27,6 +29,8 @@ use rust_core::{AddFn,StrFn};
2729
use symbol::Symbol;
2830
use crate::value::{ToValue,Evaluable};
2931
use crate::persistent_list::{PersistentList,ToPersistentList};
32+
use crate::persistent_list_map::*;
33+
use crate::maps::*;
3034
use crate::persistent_vector::{PersistentVector,ToPersistentVector};
3135
use crate::value::Value;
3236

src/maps.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//! General map utilities
2+
use crate::value::Value;
3+
use std::rc::Rc;
4+
5+
#[derive(Debug,Clone,PartialEq,Hash)]
6+
pub struct MapEntry {
7+
// We need to box key to avoid an infinite cycle of
8+
// Value::Persistent*Map { Persistent*Map { MapEntry { Value <--- cycle restarts , val }}}
9+
// Implemented with an Rc because inevitably, our system tends to live in Rcs
10+
pub key: Rc<Value>,
11+
pub val: Rc<Value>
12+
}

src/persistent_list.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ use std::fmt::Debug;
44
use std::iter::FromIterator;
55

66
use crate::value::{Value,ToValue};
7+
use std::hash::{Hash,Hasher};
78

8-
#[derive(Debug,Clone)]
9+
#[derive(Debug,Clone,PartialEq,Hash)]
910
pub enum PersistentList {
1011
// @TODO refactor i32 (our len) into a usize
1112
Cons(Rc<Value>,Rc<PersistentList>,i32),
1213
Empty
1314
}
15+
// @TODO definitely don't do this
16+
#[derive(Debug,Clone,Hash)]
17+
struct EmptyHash {}
18+
1419
use crate::persistent_list::PersistentList::{Empty,Cons};
1520
pub fn cons_rc(head: Rc<Value>, tail: Rc<PersistentList>) -> PersistentList
1621
{
@@ -151,3 +156,32 @@ impl FromIterator<Rc<Value>> for PersistentList {
151156
////////////////////////////////////////////////////////////////////////////////////////////////////
152157

153158

159+
#[cfg(test)]
160+
mod tests {
161+
use crate::persistent_list::*;
162+
#[test]
163+
fn test_persistent_list_count()
164+
{
165+
let plist = cons(1_i32.to_value(),cons(2_i32.to_value(),Empty));
166+
let plist2 = cons(1_i32.to_value(),cons(2_i32.to_value(),cons(3_i32.to_value(),Empty)));
167+
let plist3 = Empty;
168+
let plist4 = cons_rc(4_i32.to_rc_value(),Rc::new(plist2.clone()));
169+
let rc_plist4 = Rc::new(plist4.clone());
170+
let plist5 = cons_rc(5_i32.to_rc_value(),Rc::clone(&rc_plist4));
171+
172+
let vec6 = vec![1_i32.to_rc_value(),2_i32.to_rc_value(),3_i32.to_rc_value(),4_i32.to_rc_value(),5_i32.to_rc_value(),6_i32.to_rc_value()];
173+
let plist6 = vec6.into_iter().collect::<PersistentList>();
174+
let plist6_2 = Rc::new(plist6.clone()).iter().map(|rc_val| {
175+
Rc::clone(&rc_val)
176+
}).collect::<PersistentList>();
177+
178+
179+
assert_eq!(plist.len(),2);
180+
assert_eq!(plist2.len(),3);
181+
assert_eq!(plist3.len(),0);
182+
assert_eq!(plist4.len(),4);
183+
assert_eq!(plist5.len(),5);
184+
assert_eq!(plist6.len(),6);
185+
assert_eq!(plist6_2.len(),6);
186+
}
187+
}

src/persistent_list_map.rs

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
//! For very small Persistent maps; this is a persistent map implemented upon
2+
//! persistent lists, which already can share structure. In this case, each
3+
//! element of the list is a key-value pair (so this is very similar to implementing
4+
//! a persistent associative structure in Elisp with alists)
5+
//!
6+
//! (MapEntry :a 1)
7+
//! / \
8+
//! / \
9+
//!(MapEntry :b 2) (MapEntry :b 3)
10+
//! / \
11+
//! a b
12+
//! -------------------
13+
//! a => {:a 1 :b 2}
14+
//! b => {:a 1 :b 3}
15+
16+
use crate::maps::MapEntry;
17+
use crate::value::Value;
18+
use crate::symbol::Symbol;
19+
20+
use std::collections::HashMap;
21+
use std::rc::Rc;
22+
use std::fmt;
23+
use std::iter::FromIterator;
24+
use std::convert::From;
25+
26+
27+
#[derive(Debug,Clone,PartialEq,Hash)]
28+
pub enum PersistentListMap {
29+
Map(Rc<PersistentListMap>,MapEntry),
30+
Empty
31+
}
32+
// Again, only using strange IBlah convention to reflect the Clojure base
33+
// @TODO really though .. just rethink this
34+
/// A PersistentListMap.
35+
pub trait IPersistentListMap {
36+
fn get(&self,key: &Rc<Value>) -> Rc<Value>;
37+
fn assoc(&self,key: Rc<Value>, value: Rc<Value>) -> Self;
38+
}
39+
impl IPersistentListMap for PersistentListMap {
40+
// @TODO make fn of ILookup
41+
fn get(&self,key: &Rc<Value>) -> Rc<Value> {
42+
match self {
43+
PersistentListMap::Map(parent,entry) => {
44+
if entry.key == *key {
45+
return Rc::clone(&entry.val);
46+
}
47+
parent.get(key)
48+
},
49+
PersistentListMap::Empty => Rc::new(Value::Nil)
50+
}
51+
}
52+
fn assoc(&self,key: Rc<Value>, val: Rc<Value>) -> PersistentListMap {
53+
PersistentListMap::Map(Rc::new(self.clone()),MapEntry{key,val})
54+
}
55+
}
56+
57+
impl IPersistentListMap for Rc<PersistentListMap> {
58+
// @TODO make fn of ILookup
59+
fn get(&self,key: &Rc<Value>) -> Rc<Value> {
60+
match &**self {
61+
PersistentListMap::Map(parent,entry) => {
62+
if entry.key == *key {
63+
return Rc::clone(&entry.val);
64+
}
65+
parent.get(key)
66+
},
67+
PersistentListMap::Empty => Rc::new(Value::Nil)
68+
}
69+
}
70+
fn assoc(&self,key: Rc<Value>, val: Rc<Value>) -> Rc<PersistentListMap> {
71+
Rc::new(PersistentListMap::Map(Rc::clone(self),MapEntry{key,val}))
72+
}
73+
}
74+
75+
// The purpose of these functions are no longer to implement conversion,
76+
// but to give us a cleaner way to invoke it
77+
pub trait ToPersistentListMap {
78+
fn into_list_map(self) -> PersistentListMap;
79+
}
80+
impl<T> ToPersistentListMap for T where
81+
T: Into<PersistentListMap>
82+
{
83+
fn into_list_map(self) -> PersistentListMap {
84+
Into::<PersistentListMap>::into(self)
85+
}
86+
}
87+
impl From<Vec<MapEntry>> for PersistentListMap {
88+
fn from(item: Vec<MapEntry>) -> Self {
89+
item.into_iter().collect::<PersistentListMap>()
90+
}
91+
}
92+
////////////////////////////////////////////////////////////////////////////////////////////////////
93+
// Iterating
94+
//
95+
////////////////////////////////////////////////////////////////////////////////////////////////////
96+
pub struct PersistentListMapIter {
97+
node: Rc<PersistentListMap>,
98+
seen: HashMap<Rc<Value>,bool>
99+
}
100+
pub trait ToPersistentListMapIter {
101+
fn iter(&self) -> PersistentListMapIter;
102+
}
103+
impl Iterator for PersistentListMapIter {
104+
type Item = MapEntry;
105+
fn next(&mut self) -> Option<Self::Item> {
106+
match &*(Rc::clone(&self.node)) {
107+
PersistentListMap::Map(parent,mapentry) => {
108+
self.node = Rc::clone(parent);
109+
if self.seen.contains_key(&mapentry.key) {
110+
return self.next();
111+
}
112+
self.seen.insert(mapentry.key.clone(),true);
113+
Some(mapentry.clone())
114+
},
115+
PersistentListMap::Empty => None
116+
}
117+
}
118+
}
119+
120+
impl ToPersistentListMapIter for Rc<PersistentListMap> {
121+
fn iter(&self) -> PersistentListMapIter {
122+
PersistentListMapIter {
123+
node: Rc::clone(self),
124+
seen: HashMap::new()
125+
}
126+
}
127+
}
128+
impl ToPersistentListMapIter for PersistentListMap {
129+
fn iter(&self) -> PersistentListMapIter {
130+
Rc::new(self.clone()).iter()
131+
}
132+
}
133+
134+
impl FromIterator<MapEntry> for PersistentListMap {
135+
fn from_iter<I: IntoIterator<Item=MapEntry>>(iter: I) -> Self {
136+
let mut map_so_far = PersistentListMap::Empty;
137+
138+
for i in iter {
139+
map_so_far = PersistentListMap::Map(Rc::new(map_so_far),i.clone());
140+
}
141+
map_so_far
142+
}
143+
}
144+
////////////////////////////////////////////////////////////////////////////////////////////////////
145+
// End Iteration
146+
////////////////////////////////////////////////////////////////////////////////////////////////////
147+
148+
impl fmt::Display for PersistentListMap {
149+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150+
let mut as_str = String::from("{");
151+
let mut first_loop = true;
152+
153+
for mapentry in self.iter() {
154+
if !first_loop {
155+
as_str.push_str(", ");
156+
}
157+
first_loop = false;
158+
as_str.push_str(&format!("{} {}",mapentry.key.to_string_explicit(),mapentry.val.to_string_explicit()));
159+
}
160+
as_str.push_str("}");
161+
162+
write!(f, "{}",as_str)
163+
}
164+
}
165+
#[cfg(test)]
166+
mod tests {
167+
use crate::persistent_list_map::*;
168+
use crate::value::ToValue;
169+
#[test]
170+
fn test_persistent_list_map()
171+
{
172+
let empty = PersistentListMap::Empty;
173+
let map1 = vec![MapEntry { key: Symbol::intern("a").to_value(), val: 15_i32.to_rc_value()},
174+
MapEntry { key: Symbol::intern("b").to_value(), val: "stuff".to_rc_value()}].into_iter().collect::<PersistentListMap>();
175+
println!("{}",map1);
176+
let map2 = map1.assoc(Symbol::intern("c").to_value(),100_i32.to_rc_value());
177+
println!("{}",map1);
178+
println!("{}",map2);
179+
let map3 = map1.assoc(Symbol::intern("a").to_value(),100_i32.to_rc_value());
180+
println!("{}",map1);
181+
println!("{}",map2);
182+
println!("{}",map3);
183+
let map4 = map2.assoc(Symbol::intern("a").to_value(),100_i32.to_rc_value());
184+
println!("{}",map1);
185+
println!("{}",map2);
186+
println!("{}",map3);
187+
println!("{}",map4);
188+
}
189+
}

src/persistent_vector.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ use std::rc::Rc;
22
use std::fmt;
33
use std::fmt::Debug;
44
use std::iter::FromIterator;
5+
use std::hash::{Hash,Hasher};
6+
use std::convert::From;
57

68
use crate::value::{Value,ToValue};
79

8-
#[derive(Debug,Clone)]
10+
#[derive(Debug,Clone,PartialEq,Hash)]
911
pub struct PersistentVector {
1012
pub vals: Vec<Rc<Value>>
1113
}
@@ -17,11 +19,22 @@ impl fmt::Display for PersistentVector {
1719
write!(f, "[{}]",str)
1820
}
1921
}
22+
23+
impl From<Vec<Rc<Value>>> for PersistentVector {
24+
fn from(item: Vec<Rc<Value>>) -> Self {
25+
item.into_iter().collect::<PersistentVector>()
26+
}
27+
}
28+
// impl Hash for PersistentVector {
29+
// fn hash<H: Hasher>(&self, state: &mut H) {
30+
// let as_vec = Rc::new(self.clone()).iter().collect::<Vec<Rc<Value>>>();
31+
// as_vec.hash(state)
32+
// }
33+
// }
2034
//
2135
// Mostly to just make some code more concise
22-
// @TODO lookup proper rust conversion traits
23-
//
24-
/// Converts into a PersistentVector
36+
// @TODO ~lookup proper rust conversion traits~
37+
// @TODO ok, proper conversions found, start removing these
2538
pub trait ToPersistentVector {
2639
// Uses 'into' instead of typical 'to_..' because this is actually meant to be
2740
// (into [] self), a sort of building block of our eventual `into` function

0 commit comments

Comments
 (0)