# [Tutor] Trickier exercise: convert string to complex number

Lie Ryan lie.1296 at gmail.com
Sat Jul 11 06:33:58 CEST 2009

```Angus Rodgers wrote:
> Wesley Chun, /Core Python Programming/, Ex. 6-13:
>
>  "[...] An atoc() was never implemented in the string module, so
>  that is your task here.  atoc() takes a single string as input,
>  a string representation of a complex number [...] and returns
>  the equivalent complex number object with the given value [...]"

It's true that atoc() was never implemented in the string module, but
the builtin complex() does the same thing (and more).

> <http://python.pastebin.com/d6f7c679f>   (retention: 1 day)
> <http://python.pastebin.com/d2ea157ff>   (retention: 1 day)
> (helper functions for user input)
>
> The main functions are short enough to post here, I think, but
> there are many explanatory and apologetic comments in the first
> file above; also, I would welcome (with a grimace!) any comments
> as to whether I am developing a cranky or baroque style (as I'm
> working very much on my own, apart from whatever feedback I can
> get here), and such misdemeanours are more likely to be noticed
> in the second (and longer) of the two files above.
>
> from string import whitespace
>
> def no_spaces(str):
>     return ''.join([ch for ch in str if ch not in whitespace])
>
> def atoc(num):
>     """Convert string representation to complex number."""
>     num = no_spaces(num)
>     n = len(num)
>     if not n:
>         raise ValueError
>     # Ignore first character
>     for i, ch in enumerate(num[1:]):
>         if ch == 'j':
>             # Must be end of string, else invalid
>             if i != n - 2:
>                 raise ValueError
>             return complex(0.0, float(num[:-1]))
>         if ch in '+-' and num[i] not in 'eE':
>             return complex(float(num[:i + 1]),
>                            float(num[i + 1:-1]))
>     return complex(float(num), 0.0)
>

Err... what's the question? Are you asking for style checking or the

===
_const_func = lambda c : (lambda x : c)
...
def get_val(typ, prompt=None, valid=_const_func(True)):
...

I think that is an over-generalisation. It is much simpler to read
def get_val(typ, prompt=None, valid=lambda x: True):

do (whether it is simply returning a lambda or it does something more
complex).
===

> # A bug in this program is that, by indiscriminately removing all
> #
> # whitespace before processing, it fails to catch certain errors,
> #
> # e.g. " - 1 2 " will be passed to float("-12").  Correcting this
> #
> # bug is likely to make the program considerably more ... complex.

You can avoid that bug by not stripping whitespaces. Ignore them inside
the loop (put if ch == '': break) and before passing the number to
float, compare the number's length before and after the whitespace is
stripped. Not really considerably more complex...

===
> # (It would seem to be more sensible to learn about the regular
> #
> # expressions module than to complicate this simplistic program.)

With regex, you'll just "simply" need something like:

>>> import re
>>> pat = r'({s})??(?:({f})({s})?)??({f}j)?\$'
>>> pat = pat.format(f=r'(?:\d*\.?\d*)', s=r'[+-]')
>>>
>>> print pat
([+-])??(?:((?:\d*\.?\d*))([+-])?)??((?:\d*\.?\d*)j)?\$
>>>
>>> tests = ['', '10', '+10', '-10', '10.', '+10.', '-10.', '.4', '+.4',
'-.4', '10.4', '+10.4', '-10.4', '10j', '+10j', '-10j', '10.j', '+10.j',
'-10.j', '.4j', '+.4j', '-.4j', '10.4j', '+10.4j', '-10.4j', '10+10j',
'+10+10j', '-10+10j', '10.+10j', '+10.+10j', '-10.+10j', '10.4+10j',
'+10.4+10j', '-10.4+10j', '.4+10j', '+.4+10j', '-.4+10j', '10+10.4j',
'10.4+10.4j', '+10.4+10.4j', '+10.4-10.4j']
>>> for t in tests:
...     print t, re.match(pat, t).groups()
...
(None, None, None, None)
10 (None, '10', None, None)
+10 ('+', '10', None, None)
-10 ('-', '10', None, None)
10. (None, '10.', None, None)
+10. ('+', '10.', None, None)
-10. ('-', '10.', None, None)
.4 (None, '.4', None, None)
+.4 ('+', '.4', None, None)
-.4 ('-', '.4', None, None)
10.4 (None, '10.4', None, None)
+10.4 ('+', '10.4', None, None)
-10.4 ('-', '10.4', None, None)
10j (None, None, None, '10j')
+10j (None, '', '+', '10j')
-10j (None, '', '-', '10j')
10.j (None, None, None, '10.j')
+10.j (None, '', '+', '10.j')
-10.j (None, '', '-', '10.j')
.4j (None, None, None, '.4j')
+.4j (None, '', '+', '.4j')
-.4j (None, '', '-', '.4j')
10.4j (None, None, None, '10.4j')
+10.4j (None, '', '+', '10.4j')
-10.4j (None, '', '-', '10.4j')
10+10j (None, '10', '+', '10j')
+10+10j ('+', '10', '+', '10j')
-10+10j ('-', '10', '+', '10j')
10.+10j (None, '10.', '+', '10j')
+10.+10j ('+', '10.', '+', '10j')
-10.+10j ('-', '10.', '+', '10j')
10.4+10j (None, '10.4', '+', '10j')
+10.4+10j ('+', '10.4', '+', '10j')
-10.4+10j ('-', '10.4', '+', '10j')
.4+10j (None, '.4', '+', '10j')
+.4+10j ('+', '.4', '+', '10j')
-.4+10j ('-', '.4', '+', '10j')
10+10.4j (None, '10', '+', '10.4j')
10.4+10.4j (None, '10.4', '+', '10.4j')
+10.4+10.4j ('+', '10.4', '+', '10.4j')
+10.4-10.4j ('+', '10.4', '-', '10.4j')
>>>
>>> # might have just used complex()

===

```