is there an easier way to write this code pattern? - python

I am building an evaluator and often encounters this code pattern. Is there a simpler (shorter, more Pythonic, etc) way to write this? It should either return None if the list of operands is empty, the first operand if there is only one to process, and actually create an Add node by calling a function otherwise.
# l contains a list of operands to process
if not l: ans = None
elif len(l) == 1: ans = l[0]
else: ans = createAddNode(*l) # returns Add(a, b, c, ...)
return ans
I find myself repeating this code pattern for various operations like subtract, multiply, divide, etc

Since in python you do not have to specify the return type of a function, you can omit the first line.
if l:
ans = l[0] if len(l) == 1 else createAddNode(*l)
return ans

It's quite hard to answer this without context but I will try and give some suggestions
def test(l):
# l contains a list of operands to process
if not l:
ans = None
elif len(l) == 1:
ans = l[0]
else:
ans = createAddNode(*l) # returns Add(a, b, c, ...)
return ans
at the very least you should have these if statements on separate lines for readability.
if not l:
ans = None
This statement is rather redundant as you are already evaluating whatever is stored in the collection "l" to empty
elif len(l) == 1:
ans = l[0]
this suggestion depends on what "createAddNode" does but once again this seems rather redundant. I would find a way to evaluate this expression inside "createAddNode".
def test(l):
# l contains a list of operands to process
if l:
return createAddNode(*l) # returns Add(a, b, c, ...)
this would be my final function assuming you refactor createAddNode to handle the base case where length is one. This will return None if l is empty
Just as a side note I don't see a point in unpacking a variable amount of arguments in your "createAddNode" function. Odds are you can probably just do this with a for loop, but this obviously depends on what you are trying to unpack.

Related

How to check if a number in a list range

I have a float number x and a list range list_ = [[a, b], [c,d], [e,f]]
How can check if the number x is in the list. It means the function will return True in case of
a<=x <=b
or
c<=x <=d
or
e<=x <=f
Otherwise, the function will return False. Could you help me to write a Python code for the function
function (x, list_)--> True/False
Clean solution:
def function(x, list_):
return any([l[0] < x < l[1] for l in list_])
Optimized solution:
def function(x, list_):
for l in list_:
if l[0] < x < l[1]:
return True
return False
The idiomatic solution would be this:
def f(x: int, ls: List[Tuple[float, float]]) -> bool:
return any(a <= x <=b for (a, b) in ls)
Take specific note of the following:
Naming a function function is a super poor idea.
It is abnormal and therefore a poor idea to name a variable list_ just to avoid overriding a keyword.
Using the form any ensures that you quickly quit when you find a valid solution.
You can quickly destructure your tuple (or list, if you happen to pass a list) using the for (a, b) in ls clause.
This solution is as quick as if you use a for clause, but all of that is premature optimization anyway.
Using an explicit destructing ensures you have two and only two elements for your sublist.
It was requested that I check certain inputs:
>>> f(10.1, [[8.1, 12.1], [110, 120]])
True
Seems to work!
If you're running into NameError, the issue is simply one of the importation of types. You can either define f like so:
def f(x, ls):
... // As you would otherwise
Or import the required types to make the type-hinting work properly:
from typing import List, Tuple
def f(x: int, ls: List[Tuple[float, float]]) -> bool:
... // As you would otherwise
This has little to do with the original question or solution - it's just standard for type hinting in python.
def function(x,list__):
for [a,b] in list_data:
if a<=x<=b:
return True
return False
You can simply iterate through the list and find whether it's in range or not.
I'm generating the variable and the list randomly and calling a function that iterates and checks whether the variable lies within the range of any of the members of the list.
import numpy as np
def find_if_in_range(list_, var) -> bool:
for i in list_:
if i[0] <= var <= i[-1]:
return True
return False
var = np.random.randint(10)
list_ = np.random.randint(10, size=(3,2), dtype=np.uint8)
print(f"var: {var}")
print(f"list: {list_}")
res = find_if_in_range(list_, var)
print(res)
Output:
var: 0
list: [[0 6]
[2 7]
[7 9]]
True
Hope this helps.
Cheers.

Return a list recursively

I need help with recursion I have a code right here which turns an integer into a list of strings. However, I'm struggling to make it recursive. Here is what I have so far.
def turnList( a ):
b = str(a)
c = []
for digit in b:
c.append(digit)
return c
For a recursive function you need a base case, i.e. when we have finished and no longer need to recurse and a generic recursive case
def turnList(a):
a=str(a)
if len(a)==1:
return [a]
else:
return [a[0]] + turnList(a[1:])
Our base case is when our recursive function gets a string of length one. And our recursive case returns the first value in its input as a string (in a list) combined with the list of all the 'future' recursive strings.
Start with a base case, in this case empty list for zero, then define the recursive behavior.
def turnList(a):
if a == 0: # base case
return []
a,d = divmod(a,10) # strip off last digit
return turnList(a) + [str(d)] # recurse
print(turnList(123))
Output:
['1', '2', '3']
There are multiple ways to do recursion. Here, we adjust bIndex.
b = "hello"
def turnList(c, bIndex):
if len(b) == bIndex:
return c
return turnList(c + [b[bIndex]],bIndex+1)
print(turnList([],0))
global an
an = []
def turnList(a):
global an
b=str(a)
an.append(b[len(b)-1])
try:
return turnList(int(b[:len(b)-1]))
except ValueError as e:
an.reverse()
return an
m = turnList(10)
print(m)
this might be the best i've ever written

How to split a list only using car, cdr, cons and other functions (python)

we need to be able to make a function that has 1 list as an input. We need to split the even numbers from the uneven numbers and put them in seperate lists. We are not permitted to make a second list and should be able to solve it only using recursion, and with car, cdr, cons, ... .
This is what I already have:
def split_list(list_a):
if null(list_a):
return []
elif not even(car(list_a)):
return cons(car(list_a), split_list(cdr(list_a)))
else:
return cons(splits_lijst(cdr(list_a)), car(list_a))
print(split_list([1, 2, 3, 4]))
I became the output: [1, 3, 4, 2]
While it should be: [1, 3][2, 4]
I really have no clue how to do this without making a secondary list.
Just to be clear, the functions in the function 'split_list' are car, cdr, cons, null and even. Here you see the contents of those functions:
def car(a_list):
return a_list[0]
def cdr(a_list):
return a_list[1:]
def null(a_list):
return True if len(a_list) == 0 else False
def cons(a, b):
new_list = []
if type(a) is list:
for item in a:
new_list.append(item)
else:
new_list.append(a)
if type(b) is list:
for item in b:
new_list.append(item)
else:
new_list.append(b)
return new_list
def even(x):
if x == 1:
return False
elif x == 0:
return True
else:
return even(x-2)
You need a way to make two lists during your iteration of one list. The best way to do that is to make a function that takes 3 arguments. one for the input and 2 for the two output lists, often called accumulators.
The logic would be to return a list of the two accumulators when you have reached the end of the list. If not you check the element for evenness and recurse by adding it to the even accumulator. If not you recurse by adding the element to the odd accumulator.
I think this would help you.
l=list(range(10))
evens=[]
odds=[]
for x in l:
if x%2==0:
evens.append(x)
else:
odds.append(x)
print(evens,odds)
the below one will use only one additional list,(in case you don't require the original one):
l=list(range(10))
odds=[]
for x in l:
if x%2==0:
continue
else:
l.remove(x)
odds.append(x)
print(l,odds)

Writing filter() function but getting typeError

my code consists of me recreating the function 'filter()' and using it with a function to filter words longer than 5 characters. It worked with the actual function filter when I tried it btw...I'm using python 3+
def filter1(fn, a):
i = 0
while i != len(a):
u = i - 1
a[i] = fn(a[i], a[u])
i += 1
return a
def filter_long_words(l):
if len[l] > 5:
return [l]
listered = ['blue', 'hdfdhsf', 'dsfjbdsf', 'jole']
print(list(filter1(filter_long_words, listered)))
getting error
TypeError: filter_long_words() takes 1 positional argument but 2 were given
You are passing two parameters to fn (which refers to filter_long_words) here:
a[i] = fn(a[i], a[u])
But filter_long_words only accepts one parameter.
Notes:
You can loop through lists using for item in my_list, or if you want index as well for index, item in enumerate(my_list).
I think you might get an IndexError since u will be -1 in the first round of your loop.
The filter function can also be expressed as a list comprehension: (item for item in listered if filter_long_words(item))
My version of filter would look like this, if I have to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
for item in sequence:
if fn(item):
yield item
Since you have stated that you are using Python 3, this returns a generator instead of a list. If you want it to return a list:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
acc = []
for item in sequence:
if fn(item):
acc.append(item)
return acc
If you don't need to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
return (item for item in sequence if fn(item))
Your're calling fn with 2 parameters in filter1(fn, a), and since you've passed filter_long_words() to filter1 as fn, that triggers the error.
But there's more weird stuff:
I don't understand the magick of filter1 or what you were trying to
accomplish, but it seems to me that you don't have a clear idea what to do.
But if you want to mimic (somehow) how filter works, you have to return a
list which contains only items for which the fn function returns true. When
you know this, you can rewrite it - here are a few suggestions for rewrite
# explicit, inefficient and long, but straightforward version:
def filter1(fn, a):
new_list = []
for item in a:
if fn(item):
new_list.append(item):
return new_list
# shorter version using list comprehensions:
def filter1(fn, a):
return [item for item in a if fn(item)]
The filter_long_words function is wrong too - it should return True or
False. The only reason why it could work is because any non-empty list is
treated as True by python and default return value of a function is None,
which translates to False. But it's confusing and syntactically wrong to use
len[l] - the proper usage is len(l).
There are a few suggestions for rewrite, which all returns explicit boolean
values:
# unnecessary long, but self-explanatory:
def filter_long_words(l):
if len(l) > 5:
return True
else
return False
# short variant
def filter_long_words(l):
return len(l) > 5
You are calling "filter_long_words" with 2 parameter => fn(a[i], a[u]) also there is an error
def filter_long_words(l):
if **len[l]** > 5:
return [l]
len is builtin method it should be len(l)

Equivalent of `return` for Python generators

Sometimes, when rewriting recursive functions as generators, I miss the brevity of return.
"""
Returns a list of all length n strings that can be made out of a's and/or b's.
"""
def ab_star(n):
if n == 0:
return [""]
results = []
for s in ab_star(n - 1):
results.append("a" + s)
results.append("b" + s)
return results
turns into
"""
Generator for all length n strings that can be made out of a's and/or b's.
"""
def ab_star(n):
if n == 0:
yield ""
else:
for s in ab_star(n - 1):
yield "a" + s
yield "b" + s
It's that else that bugs me. I wish there was a way to say "yield, and this is it, so exit the function". Is there a way?
Don't miss return, use it.
You can return right after you yield.
def ab_star(n):
if n == 0:
yield ""
return
for s in ab_star(n - 1):
yield "a" + s
yield "b" + s
An alternative is to use return in both cases, where the first case returns a sequence of length 1, and the second returns a generator-expression:
def ab_star(n):
if n == 0:
return ( "", )
return ( c+s for s in ab_star(n - 1) for c in 'ab' )
This avoidance of yield avoids the limitation that you cannot use both return <value> and yield in the same function.
(This works in your case because your function doesn't have to be a generator. Since you only iterate over the results, it can also return a tuple.)
There isn't. When I wrote the "Simple Generators PEP", I noted:
Q. Then why not allow an expression on "return" too?
A. Perhaps we will someday. In Icon, "return expr" means both "I'm
done", and "but I have one final useful value to return too, and
this is it". At the start, and in the absence of compelling uses
for "return expr", it's simply cleaner to use "yield" exclusively
for delivering values.
But that never gained traction. Until it does ;-), you can make your generator look more like your first function by writing the first part as:
if n == 0:
yield ""
return
Then you can drop the else: statement and dedent the rest.

Categories