I have a list of octal numbers that I want to be converted to decimal. Here's my class with what I've done so far:
class Octal:
#Reads in the file into numberList, converting each line into an int.
def __init__(self):
list = []
file = open("Number Lists/random_numbers3.txt")
for line in file:
list.append(int(line))
self.numberList = list
file.close()
#Convert numberList to decimal
def dec_convert(self):
decimal = 0
decimalList = []
for line in self.numberList:
temp = str(line)
i = 0
while i < len(temp):
digit = int(temp[i])
item = (digit * (8 ** (len(temp) - i)))
decimal = decimal + item
i += 1
decimalList.append(decimal)
return decimalList
def get_list(self):
return self.numberList
I read in the numbers from a file, that works fine. But I don't think that my dec_convert() function actually works. It just keeps running and doesn't finish.
It looks completely terrible and hard to read, so I was wondering if there was a simpler way of converting each octal number in the list to a decimal number?
Here is an easy solution that uses the built-in int() constructor rather your dec_convert() function.
class Octal:
def __init__(self):
with open("Number Lists/random_numbers3.txt") as fp:
self.numberList = map(lambda x:int(x,8), fp)
def get_list(self):
return self.numberList
if __name__=="__main__":
o = Octal()
print(o.get_list())
Yes, you could use list comprehension:
def dec_convert(self):
decimalList = [self._convert_to_dec(line) for line in self.numberList]
and with:
def _convert_to_dec(self,dec) :
n = len(temp)-1
return sum(int(x)*(8**(n-i)) for i,x in enumerate(dec))
The first code fragment is a simple list comprehension that calls self._convert_to_dec on all elements in `self.numberList. Not much magic there.
The _convert_to_dec is more complicated: we first calculate the amount of digits and store it in n. Next we define a generator that enumerates over the characters and binds i to the corresponding index. The generator multiplies each element with the corresponding power of 8 and the digit. This is a generator, so no real list is constructed.
By running this through sum we obtain the sum which is the requested result.
Or as #TomKarzes says, you can use int with a given base (in this case 8.
Related
def toBinary(decimal, binaryList):
if decimal <= 1:
return binaryList
else:
decimal = decimal //2
return toBinary(decimal, binaryList)
binaryList.append(decimal %2)
The functions returns empty brackets instead of printing the binary number as a list.
Your code is on the right track but has issues. First is what #KlausD comments, no line directly after a return gets executed. So the order of statements is at issue. Next, the variable binaryList doesn't get initialized in your provided code fragment. It could get initialized by the caller:
array = toBinary(13, []):
which works, but the caller might find it strange. We could intialize it using a default:
def toBinary(13, binaryList=[]):
But as a container type, that would constitute a dangerous default which we want to steer clear of. We could safely initialize it to None as a default and reinitialize it later in the code:
def toBinary(decimal, binaryList=None):
# ...
if binaryList is None:
binaryList = []
Which is safe and hides this argument from the caller. Next, by dividing, we're analyzing our decimal digits from right to left, so we need to build up our binary number in the same direction, thus append():
binaryList.append(decimal % 2)
is a problem as it builds up the binary result from left to right. We could end with a revese() but it's probably better to use binaryList.insert(0, ...) to build in the proper direction. Finally, this is a special case:
array = toBinary(0)
as we're going to use a zero argument to trigger end of recursion but how it affects our result (i.e. not at all) is different than if we're passed zero from the get-go (i.e. return a [0]). Here's my rework of your code that addresses all of these issues:
def toBinary(decimal, binaryList=None):
if decimal == 0:
return binaryList or [0]
if binaryList is None:
binaryList = []
binaryList.insert(0, decimal % 2)
return toBinary(decimal // 2, binaryList)
print(toBinary(13))
OUTPUT
> python3 test.py
[1, 1, 0, 1]
>
It is better not to return binary list.
def toBinary(decimal, binaryList):
if decimal <= 0:
return
else:
toBinary(decimal//2, binaryList)
binaryList.append(decimal %2)
This code also converts to Binary. It can also convert into other bases. Just change the parameter base, to the base you want.
import string
def to_base(value, base=2): # converts decimal to base n
string_slice = string.printable[0:base]
data_dict = {}
for each_character, each_number in zip(string_slice, range(base)):
data_dict.update({each_character: each_number})
data = []
temporary_var = value
data.append(temporary_var)
while True:
temporary_var = temporary_var // base
data.append(temporary_var)
if temporary_var < base:
break
else:
continue
result = ''
for each_data in data:
result += list(data_dict.keys())[each_data % base]
result = result[::-1]
return result by
My code may not be perfect. Feel free to suggest improvements.
print(to_base(5, base=2)) # Outputs : 101. Because 101 is 5 in binary
print(to_base(17, base=16)) # Outputs : 11. Because 11 is 17 in hexadecimal
I've tried to write a simple function, which input is binary number in string format and converts binary to decimal. But in the output I always get the wrong thing: the 'res' value in line 3, no matter what the input is ('1010', '10010111010', etc.). Also, I've tried to debug the code and the function doesn't even start the loop, as if it wasn't there... So, I just don't see my mistake
def bin_to_dec(bin):
bin = bin[::-1]
res = 0
for i in range(len(bin)):
if bin[i] == 0:
res += 2**i
return res
You are comparing the string "0" to the number 0 and they are, trivially, unequal.
So, contrary to what you say, the loop is actually looping; but the if statement will never be true.
Of course, also, you should probably add when the number is 1, not when it's 0.
def bin_to_dec(bin):
bin = bin[::-1]
res = 0
for i in range(len(bin)):
if int(bin[i]) == 1:
res += 2**i
return res
Notice the addition of int().
if bin[i] == '1'
This will correct the problem. bin[i] is a character and you are comparing it to a number which always results in false.
You can just use the built in int function:
def binaryToDecimal(n):
return int(n,2)
For example, if I want to detect all odd numbers in an array and set them to zero, I can use:
def setToZeroIfOdd(n):
if n % 2 == 0:
pass
else:
return 0
numbers = range(1,1000)
numbers = map(setToZeroIfOdd, numbers)
which works like a charm.
But when I try something like
def setToZeroIfDivisibleBy(n, divisor):
if n % divisor == 0:
return 0
else:
pass
numbers = map(setToZeroIfDivisibleBy(divisor=3), numbers)
it expects two arguments. Likewise,
numbers = map(setToZeroIfDivisibleBy, numbers, divisor=3)
does not work. How can I pass that divisor argument from within map()?
You can use functools.partial to make partial functions
from functools import partial
def setToZeroIfDivisibleBy(n, divisor):
if n % divisor == 0:
return 0
else:
pass
numbers = range(1,1000)
numbers = map(partial(setToZeroIfDivisibleBy, divisor=3), numbers)
Try using lambda function
numbers = map(lambda n: setToZeroIfDivisibleBy(n, divisor=3), numbers)
And rather than pass did you mean return n?
You make a function which returns a function:
def setToZeroIfDivisibleBy(divisor):
def callback(n):
if n % divisor == 0:
return 0
else:
pass
return callback
numbers = map(setToZeroIfDivisibleBy(3), numbers)
BTW, you can entirely omit empty branches like else: pass; it doesn't do anything. Since it results in a None, I don't think that's what you want either. You probably want return n there instead.
Another approach, instead of using partial, is to supply an infinite (or at least, long enough) sequence of 2nd arguments for the two-argument function:
from itertools import repeat
numbers = map(setToZeroIfDivisibleBy, numbers, repeat(3))
In Python 2, map will append None as necessary to the shorter of the two sequences to make them the same length. Assuming that will cause problems (either because your function cannot handle None as an input value or you end up with an infinite loop), you can either use itertools.imap, which stops after exhausting the shorter sequence:
from itertools import imap, repeat
numbers = list(imap(setToZeroIfDivisibleBy, numbers, repeat(3)))
or pass the length of numbers as a second argument to repeat so that the two sequences are the same length.
from itertools import repeat
numbers = map(setToZeroIfDivisibleBy, numbers, repeat(3, len(numbers)))
So I've recently picked up John Guttag's Introduction to Computation and Programming Using Python,the revised and expanded edition, after having worked through most of LPTHW. I am using the book in conjunction with MIT OCW 006. Now, I was trying to complete one of the Finger Exercises listed in the book, specifically the one of page 85, chapter 7, where the author asks you to implement a function using a try-except block:
def sumDigits(s):
"""Assumes s is a string
Returns the sum of the decimal digits in s
For example, if is is'a2b3c' it returns 5"""
This is my code:
def sumDigits(s):
try:
total = 0
list1 = [s]
new_list = [x for x in list1 if x.isdigit()]
for e in new_list:
total += new_list[e]
return total
except TypeError:
print "What you entered is not a string."
When I run this program in the IDLE using a test input, the total is always computed to be zero, indicating that none of the elements of new_list are being passed to the accumulator. Could someone suggest why that is? Thanks.
It seems like the errors have been pointed out already by Rafael but it is still important to note that the more pythonic way to approach this would be:
return sum([int(x) for x in s if x.isdigit()])
There are actually several errors with your code.
Let's break them down in detail
The main problem is located in these lines:
list1 = [s]
new_list = [x for x in list1 if x.isdigit()]
You should loop directly over the string first
new_list = [x for x in s if x.isdigit()] #s is the input string
When you create a new list as you did, the variable x in x for x in list1 will take place as elements of the list. So, in your case, the list will have only one element, which happen to be whole string (because you defined the list as [s]. As the whole string is not a digit, new_list will be an empty list.
That is why you are getting 0 as a return.
However, if you loop through the string directly, x will take place as each letter in the string, and then it will be possible to check whether x is digit or not.
It is also important to highlight that new_list[e] will raise IndexError. You should correct that for e only. The sintax of for e in new_list makes the local variable e assume each value inside the list, so you do not have to get the value via indexes: you can use e directly.
Finally, in order to sum the values in your new_list, the values should be integers (int) and not string (str), so you have to cast the values to int before summing (or, you can cast each element to int during the list comprehension, by using int(x) for x in s if x.isdigit() instead of x for x in s if x.isdigit()). Also, in order to check if the input is a string or not, you better use isinstance(s, basestring) if you're in python2, or isinstance(s, str) if you're using python3.
So the whole code would look like this :
def sumDigits(s):
if isinstance(s, basestring):
total = 0
new_list = [x for x in s if x.isdigit()]
for e in new_list:
total += int(e)
return total
else:
print "What you entered is not a string."
I'm working through the same book and the MITx: 6.00.1x course on edX; here's my solution:
def sumDigits(s):
'''
Assumes s is a string
Returns the sum of the decimal digits in s
For example, if s is 'a2b3c' it returns 5
'''
result = 0
try:
for i in range(len(s)):
if s[i].isdigit():
result += int(s[i])
return result
except:
print('Your input is not a string.')
Since we are to assume that s is a string, the except block should handle those cases where s is not a string. So simple, but it was not obvious to me at first.
You can use reduce method
reduce( (lambda x, y: x + y), [int(x) for x in new if x.isdigit()] )
I'm working through the same book too. I think we should use the try-except block on determining whether characters of string convertible to an integer. So here is my solution.
def sumDigits(s):
"""Assumes s is a string
Returns the sum of the decimal digits in s
For example, if s is 'a2b3c' it returns 5"""
sum = 0
for i in s:
try:
sum += int(i)
except ValueError:
None
return sum
I don't know, why isn't working that code in python. The problem is, that I can get numbers, but sometimes some numbers are uniform. And I want to do 5 different numbers between 1 and 90.
class lottery:
def __init__(self):
self.price = 50
def list(self):
numbers = []
for i in range(0,5):
numbers.append(random.randint(0,90))
for i in range(1,5):
for j in range(0,i-1):
if (numbers[i]==numbers[j]):
game.list()
return numbers
game = lottery()
game.list()
Or is there any better way to solve my problem?
Thanks!
Use random.sample:
def list(self):
return random.sample(xrange(90), 5)
This is (especially for large values of 5) much more efficient than starting over every time your randomization creates a repeat, and also avoids the possibility of overflowing the stack.
First of all you should import the random module:
import random
The problem is that you are not returning the result that you get from the recursive call, therefore you are still returning the list with repeated numbers. It should be:
def list(self):
numbers = []
for i in range(0, 5):
numbers.append(random.randint(0, 90))
for i in range(1, 5):
for j in range(0, i - 1):
if (numbers[i] == numbers[j]):
return self.list() # return
return numbers
note that self is used to access to the instance of the class that calls the method. Also, don't forget to print the results:
game = lottery()
print game.list()
Note:
Don't use list as the name of a variable or method because it will hide the built-in definition of list.