"Removing" a node from a functional linked list - python

I'm looking for a function that returns a linked list that doesn't contain a specific node.
Here is an example implementation:
Nil = None # empty node
def cons(head, tail=Nil):
""" Extends list by inserting new value. """
return (head, tail)
def head(xs):
""" Returns the frst element of a list. """
return xs[0]
def tail(xs):
""" Returns a list containing all elements except the first. """
return xs[1]
def is_empty(xs):
""" Returns True if the list contains zero elements """
return xs is Nil
def length(xs):
"""
Returns number of elements in a given list. To find the length of a list we need to scan all of its
elements, thus leading to a time complexity of O(n).
"""
if is_empty(xs):
return 0
else:
return 1 + length(tail(xs))
def concat(xs, ys):
""" Concatenates two lists. O(n) """
if is_empty(xs):
return ys
else:
return cons(head(xs), concat(tail(xs), ys))
How can a remove_item function be implemented?

def remove_item(xs, value):
if is_empty(xs):
return xs
elif head(xs) == value:
return tail(xs) # or remove_item(tail(xs), value) to remove all
else:
return cons(head(xs), remove_item(tail(xs), value))
Note: I am not a Lisp programmer, I haven't necessarily done this the best possible way.
[Edit: I might have misinterpreted what you meant by removing a specific node. If you're starting with a suffix of xs rather than a value in xs then the principle is the same but the test involving value is different]

If you want a tail-recursive solution, you can say:
def remove_item(xs, value):
before_rev, after = split_remove(Nil, xs, value)
return reverse_append(before_rev, after)
def reverse_append(a, b):
if is_empty(a):
return b
else:
return reverse_append(tail(a), cons(head(a),b))
def split_remove(before_rev, xs, value):
if is_empty(xs):
return (before_rev, xs)
elif head(xs) == value:
return (before_rev, tail(xs))
else:
return split_remove(cons(head(xs), before_rev), tail(xs), value)
Although I don't know if Python does tail-call optimization

Related

Return smaller values in a BST inorder

I am trying to implement this method, "smaller" for a BST, that returns the values in the tree which are smaller than a given item, in order.
class BinarySearchTree:
def __init__(self, root: Optional[Any]) -> None:
if root is None:
self._root = None
self._left = None
self._right = None
else:
self._root = root
self._left = BinarySearchTree(None)
self._right = BinarySearchTree(None)
def is_empty(self) -> bool:
return self._root is None
def smaller(self, item: Any) -> List:
if self.is_empty():
return []
else:
return self._left.items() + [self._root] + self._right.items()
So far, the "smaller" method will return all of the values in the tree in order, but I'm not sure how to check if those values are smaller and than a given item, and to only return those in a list.
Let's write pseudocode for in-order-tree-walk method which prints the keys of BST in sorted (in-order) order.
in-order-tree-walk(T, x)
if (T != NULL)
in-order-tree-walk(T.left, x)
print T's key
in-order-tree-walk(T.right, x)
smaller method has exactly the same structure as in-order-tree-walk except that it's additional condition which makes it to print keys that are smaller. smaller method's pseudocode will look like
smaller(T, x)
if (T != NULL)
smaller(T.left, x)
if (T's key is less than x)
print T's key
smaller(T.right, x)
We're done. smaller method is now completed. Now let's look at your actual implementation.
Your code prints all keys of BST in sorted order because of the way you implemented it. You have the problem the following part:
def smaller(self, item: Any) -> List:
if self.is_empty():
return []
else:
return self._left.items() + [self._root] + self._right.items()
In return self._left.items() + [self._root] + self._right.items(), you don't check whether the [self.root] is less than item's value or not. You have to check that because you put restraint on printing the key of tree, but in implementation you didn't check it. Since I'm not qualified in Python, I can't complete this part, but I think you've get what the problem is with your code based on above explanations.

Custom comparator for building PriorityQueue in Python

Iam trying to build a priority queue using PriorityQueue in Python, but instead of element to be considered for priority comparison, I want it to use the return value from a function after passing the element to the function , similar to sorted(mtlist,key = myfun), is there a way to achieve this,
Rather than inserting your elements directly into the queue, wrap each element in a tuple, where the first element in the tuple is the desired sorting key. Tuples are sorted by in order of their elements (i.e., first element is compared first), hence why the sorting key needs to come first.
import heapq
queue = []
my_list = [...]
for element in my_list:
heapq.heappush(queue, (my_func(element), element))
If you have a wrapper class for the elements, then you can use operator overloading.
For example, lets say you have a CustomNumber class (equivalent to your elements) where the order is determined by the modulo 16 value (the private function __f()), the you can override the comparison operators like:
class CustomNumber:
def __init__(self, value):
self.value = value
def __f(self, x):
return x % 16
def __lt__(self, obj):
"""self < obj."""
return self.__f(self.value) < self.__f(obj.value)
def __le__(self, obj):
"""self <= obj."""
return self.__f(self.value) <= self.__f(obj.value)
def __eq__(self, obj):
"""self == obj."""
return self.__f(self.value) == self.__f(obj.value)
def __ne__(self, obj):
"""self != obj."""
return self.__f(self.value) != self.__f(obj.value)
def __gt__(self, obj):
"""self > obj."""
return self.__f(self.value) > self.__f(obj.value)
def __ge__(self, obj):
"""self >= obj."""
return self.__f(self.value) >= self.__f(obj.value)
Such that the following code:
a = CustomNumber(16)
b = CustomNumber(14)
print('a < b =', a < b)
print('a <= b =', a <= b)
print('a == b =', a == b)
print('a != b =', a != b)
print('a > b =', a > b)
print('a >= b =', a >= b)
prints:
a < b = True
a <= b = True
a == b = False
a != b = True
a > b = False
a >= b = False
Here is an example of using custom sort in PriorityQueue in Python.
We use a priority-queue (heapq) find the next element to add. To make the
implementation simple we "monkey patch" the ListNode class to have a custom
less-than function using setattr. Note that, simply using the tuple trick
and pushing (node.val, node) to the priority queue will not work because
the lists have values in common.
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
setattr(ListNode, "__lt__", lambda self, other: self.val <= other.val)
pq = []
for l in lists:
if l:
heapq.heappush(pq, l)
out = ListNode(None)
head = out
while pq:
l = heapq.heappop(pq)
head.next = l
head = head.next
if l and l.next:
heapq.heappush( pq, l.next)
return out.next
The way you wrote your question, there is no way to achieve it. According to the documentation:
The lowest valued entries are retrieved first (the lowest valued entry is the one returned by sorted(list(entries))[0]). A typical pattern for entries is a tuple in the form: (priority_number, data).
In other words, the priority is defined by running sorted on the entries, and there is no way there to define the key parameter for that sorted run.
So, you cannot set a sort function when defining the PriorityQueue. You have to use one of the other solutions provided (or write your own PriorityQueue implementation, which should not be too hard).
Edit
After checking the code, I see that the documentation is not an exact description of how it works, but a simplification.
However, it also shows how easy it would be for you to make your own implementation.

How to find two items of a list with the same return value of a function on their attribute?

Given a basic class Item:
class Item(object):
def __init__(self, val):
self.val = val
a list of objects of this class (the number of items can be much larger):
items = [ Item(0), Item(11), Item(25), Item(16), Item(31) ]
and a function compute that process and return a value.
How to find two items of this list for which the function compute return the same value when using the attribute val? If nothing is found, an exception should be raised. If there are more than two items that match, simple return any two of them.
For example, let's define compute:
def compute( x ):
return x % 10
The excepted pair would be: (Item(11), Item(31)).
You can check the length of the set of resulting values:
class Item(object):
def __init__(self, val):
self.val = val
def __repr__(self):
return f'Item({self.val})'
def compute(x):
return x%10
items = [ Item(0), Item(11), Item(25), Item(16), Item(31)]
c = list(map(lambda x:compute(x.val), items))
if len(set(c)) == len(c): #no two or more equal values exist in the list
raise Exception("All elements have unique computational results")
To find values with similar computational results, a dictionary can be used:
from collections import Counter
new_d = {i:compute(i.val) for i in items}
d = Counter(new_d.values())
multiple = [a for a, b in new_d.items() if d[b] > 1]
Output:
[Item(11), Item(31)]
A slightly more efficient way to find if multiple objects of the same computational value exist is to use any, requiring a single pass over the Counter object, whereas using a set with len requires several iterations:
if all(b == 1 for b in d.values()):
raise Exception("All elements have unique computational results")
Assuming the values returned by compute are hashable (e.g., float values), you can use a dict to store results.
And you don't need to do anything fancy, like a multidict storing all items that produce a result. As soon as you see a duplicate, you're done. Besides being simpler, this also means we short-circuit the search as soon as we find a match, without even calling compute on the rest of the elements.
def find_pair(items, compute):
results = {}
for item in items:
result = compute(item.val)
if result in results:
return results[result], item
results[result] = item
raise ValueError('No pair of items')
A dictionary val_to_it that contains Items keyed by computed val can be used:
val_to_it = {}
for it in items:
computed_val = compute(it.val)
# Check if an Item in val_to_it has the same computed val
dict_it = val_to_it.get(computed_val)
if dict_it is None:
# If not, add it to val_to_it so it can be referred to
val_to_it[computed_val] = it
else:
# We found the two elements!
res = [dict_it, it]
break
else:
raise Exception( "Can't find two items" )
The for block can be rewrite to handle n number of elements:
for it in items:
computed_val = compute(it.val)
dict_lit = val_to_it.get(computed_val)
if dict_lit is None:
val_to_it[computed_val] = [it]
else:
dict_lit.append(it)
# Check if we have the expected number of elements
if len(dict_lit) == n:
# Found n elements!
res = dict_lit
break

Does this type of recursion have a name?

I have a linked list in python and I want to write a filter function that returns a new link list if a call to f(item) is true, this implementation has a filtered that builds the list from the bottom up. I'm having trouble understanding this recursion. What type of recursion is this?
I'm more familiar with recursion like fibonacci where the return recursion is at the very bottom.
class Link:
empty = ()
def __init__(self, first, rest=empty):
assert rest is Link.empty or isinstance(rest, Link)
self.first = first
self.rest = rest
def __getitem__(self, i):
if i == 0:
return self.first
else:
return self.rest[i-1]
def __len__(self):
return 1 + len(self.rest)
def __repr__(self):
if self.rest == Link.empty:
return "Link(" + str(self.first) + ")"
return 'Link({0}, {1})'.format(self.first, repr(self.rest))
def filter_link(f, s):
if s is Link.empty:
return s
else:
filtered = filter_link(f,s.rest) # How does this work?
if f(s.first):
return Link(s.first, filtered)
else:
return filtered
This is the sort of recursion you are used to.
I just looked up a recursive fibonacci solution where the early return is on the second line, just like your code. Also, like your code, the recursion in the example occurs before the more normal returns.
It looks like your code returns a new linked list of the elements that the function f approves of, from the bottom up. That is, it creates new instances of Link around elements s.first, terminated by the single instance of Link.empty.

Dynamic list that automatically expands

How can I make a Python equivalent of pdtolist from Pop-11?
Assume I have a generator called g that returns (say) integers one at a time. I'd like to construct a list a that grows automatically as I ask for values beyond the current end of the list. For example:
print a # => [ 0, 1, 2, g]
print a[0] # => 0
print a[1] # => 1
print a[2] # => 2
# (obvious enough up to here)
print a[6] # => 6
print a # => [ 0, 1, 2, 3, 4, 5, 6, g]
# list has automatically expanded
a = a[4:] # discard some previous values
print a # => [ 4, 5, 6, g]
print a[0] # => 4
Terminology - to anticipate a likely misunderstanding: a list is a "dynamic array" but that's not what I mean; I'd like a "dynamic list" in a more abstract sense.
To explain the motivation better, suppose you have 999999999 items to process. Trying to fit all those into memory (in a normal list) all at once would be a challenge. A generator solves that part of the problem by presenting them one at a time; each one created on demand or read individually from disk. But suppose during processing you want to refer to some recent values, not just the current one? You could remember the last (say) ten values in a separate list. But a dynamic list is better, as it remembers them automatically.
This might get you started:
class DynamicList(list):
def __init__(self, gen):
self._gen = gen
def __getitem__(self, index):
while index >= len(self):
self.append(next(self._gen))
return super(DynamicList, self).__getitem__(index)
You'll need to add some special handling for slices (currently, they just return a normal list, so you lose the dynamic behavior). Also, if you want the generator itself to be a list item, that'll add a bit of complexity.
Just answered another similar question and decided to update my answer for you
hows this?
class dynamic_list(list):
def __init__(self,num_gen):
self._num_gen = num_gen
def __getitem__(self,index):
if isinstance(index, int):
self.expandfor(index)
return super(dynamic_list,self).__getitem__(index)
elif isinstance(index, slice):
if index.stop<index.start:
return super(dynamic_list,self).__getitem__(index)
else:
self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start)
return super(dynamic_list,self).__getitem__(index)
def __setitem__(self,index,value):
if isinstance(index, int):
self.expandfor(index)
return super(dynamic_list,self).__setitem__(index,value)
elif isinstance(index, slice):
if index.stop<index.start:
return super(dynamic_list,self).__setitem__(index,value)
else:
self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start)
return super(dynamic_list,self).__setitem__(index,value)
def expandfor(self,index):
rng = []
if abs(index)>len(self)-1:
if index<0:
rng = xrange(abs(index)-len(self))
else:
rng = xrange(abs(index)-len(self)+1)
for i in rng:
self.append(self._num_gen.next())
Many thanks to all who contributed ideas! Here's what I have gathered together from all the responses. This retains most functionality from the normal list class, adding additional behaviours where necessary to meet additional requirements.
class DynamicList(list):
def __init__(self, gen):
self.gen = gen
def __getitem__(self, index):
while index >= len(self):
self.append(next(self.gen))
return super(DynamicList, self).__getitem__(index)
def __getslice__(self, start, stop):
# treat request for "last" item as "most recently fetched"
if stop == 2147483647: stop = len(self)
while stop > len(self):
self.append(next(self.gen))
return super(DynamicList, self).__getslice__(start, stop)
def __iter__(self):
return self
def next(self):
n = next(self.gen)
self.append(n)
return n
a = DynamicList(iter(xrange(10)))
Previously generated values can be accessed individually as items or slices. The recorded history expands as necessary if the requested item(s) are beyond the current end of the list. The entire recorded history can be accessed all at once, using print a, or assigned to a normal list using b = a[:]. A slice of the recorded history can be deleted using del a[0:4]. You can iterate over the whole list using for, deleting as you go, or whenever it suits. Should you reach the end of the generated values, StopIteration is raised.
Some awkwardness remains. Assignments like a = a[0:4] successfully truncate the history, but the resulting list no longer auto-expands. Instead use del a[0:4] to retain the automatic growth properties. Also, I'm not completely happy with having to recognise a magic value, 2147483647, representing the most recent item.
Thanks for this thread; it helped me solve my own problem. Mine was a bit simpler: I wanted a list that automatically extended if indexed past its current length --> allow reading and writing past current length. If reading past current length, return 0 values.
Maybe this helps someone:
class DynamicList(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __getitem__(self, idx):
self.expand(idx)
return super().__getitem__(idx)
def __setitem__(self, idx, val):
self.expand(idx)
return super().__setitem__(idx, val)
def expand(self, idx):
if isinstance(idx, int):
idx += 1
elif isinstance(idx, slice):
idx = max(idx.start, idx.stop)
if idx > len(self):
self.extend([0] * (idx - len(self)))

Categories