1

it doesn't matter what the input is, the output is always 0, like the global variables NEVER change. please help me I'm going crazy because i don't see any reason for this to show only 0

x=0
y=0
import math
import operator


def move(steps,op_func,z):
    if z == "x":
        global x
        x = op_func(x, int(steps))
    else:
        global y
        y = op_func(y, int(steps))

def main():

    user_direct=raw_input("Enter the way that you want the plane to move and how many steps you want it to move\n")
    while user_direct != " ":

        steps=user_direct[-1]
        direction=user_direct[:-1]
        directions = {"UP":move(steps,operator.add,"x"),
                      "DOWN":move(steps,operator.sub,"x"),
                      "RIGHT":move(steps,operator.add,"y"),
                      "LEFT":move(steps,operator.sub,"y")}

        directions[direction.replace(" ","")]

        user_direct=raw_input("Enter the way that you want the plane to move and how many steps you want it to move\n")

        global x
        global y
        distance=math.sqrt(x**2+y**2)
        print distance
main()
7
  • 1
    What is op_func and z? Commented Nov 20, 2015 at 19:46
  • 2
    when you initialize your dictionary, you are actually calling each of those functions, not just making a reference to it. ie, the value for each key is going to be the return value of the function (NoneType) and you are moving up/down/left/right every singly time Commented Nov 20, 2015 at 19:46
  • You should find your answer here stackoverflow.com/questions/15239783/… Commented Nov 20, 2015 at 19:54
  • 2
    @notorious not even close Commented Nov 20, 2015 at 19:55
  • Perhaps op_func() always returns 0? Commented Nov 20, 2015 at 19:55

2 Answers 2

2

As my comment says, your dict initialization is actually running those functions and setting values as the return values (NoneType). Do something like this instead.

1.) Initiate your dict OUTSIDE your while loop because you dont need to reinitialize every time.

2.) change it so the value is a tuple of your parameters like:

directions = {"UP":(operator.add,"x"),
              "DOWN":(operator.sub,"x"),
              "RIGHT":(operator.add,"y"),
              "LEFT":(operator.sub,"y")}

note that the dict does not include steps because you are going to use that as a variable for every call.

3.) change your function call by using this line:

move(steps,directions[direction][0],directions[direction][1])

in place of your current dict init/dict call.

The problem with this is that if your command is NOT a valid command, it will cause errors so I would put everything in a Try block like:

try:
    move(steps,directions[direction][0],directions[direction][1])
except KeyError:
    print('Not a valid key, try again')
else:
    #here is where you put the math code you do to edit the globals
Sign up to request clarification or add additional context in comments.

2 Comments

Good call with the try/except block.
Thank you! I have just started to learn python and I didn't use dicts before, and know I know how it works!
1

You are doing these things wrong:

  • You're initializing your dictionary not with functions that can be called, but with the results of four function calls. What you really want to do is create a partial.
  • You are initializing your dictionary inside your while loop.

Additionally, storing that information globally isn't really what you want to be doing, but rather storing it within the scope of the while loop.

Lets look at what your dictionary definition is doing:

directions = {"UP":move(steps,operator.add,"x"),
              "DOWN":move(steps,operator.sub,"x"),
              "RIGHT":move(steps,operator.add,"y"),
              "LEFT":move(steps,operator.sub,"y")}

Each of these assignments calls move with the appropriate values, and sets them to the value. However, move() returns None because there is no return statement set for those: the globals are updated inside the function. So after one while loop, your directions array looks like this:

{"UP": None, "DOWN": None, "RIGHT": None, "LEFT": None}

And your x and y global values have been incremented and decremented once each. You can prove this by replacing your move function with the following:

def move(steps,op_func,z):
    if z == "x":
        global x
        x = op_func(x, int(steps))
        print("x is now {}".format(x))
    else:
        global y
        y = op_func(y, int(steps))
        print("y is now {}".format(y))

In the REPL this is what you see:

>>> y= 0
>>> x= 0
>>> steps = 1
>>> directions = {"UP":move(steps,operator.add,"x"),
>>>               "DOWN":move(steps,operator.sub,"x"),
...               "RIGHT":move(steps,operator.add,"y"),
...               "LEFT":move(steps,operator.sub,"y")}
... x is now 1
x is now 0
y is now 1
y is now 0

However, partials can help:

>>> f = partial(move, op_func=operator.add, z="x")
>>> f(1)
>>> x is now 1

Using the above, you want do define your directions map like so:

directions = {"UP":partial(move, op_func=operator.add, z="x"),
              "DOWN":partial(move, op_func=operator.sub, z="x"),
              "RIGHT":partial(move, op_func=operator.add, z="y"),
              "LEFT":partial(move, op_func=operator.sub, z="y")}

What this does is replace each "key" in your dictionary with a "partial function". A partial function as some of it's parameters 'filled in', and later on you can call that function with only the remaining value. Formally:

partial(f(a, b, c), b, c) -> g(a)

Whenever you call g, b, and c will be consistently defined for a function call to f.

Now all you have to do is change this line:

directions[direction.replace(" ","")]

To this:

move_func = directions[direction.replace(" ","")]
move_func(steps)

Re-writing and cleaning up the program a bit yields:

import math
import operator
from functools import partial
# Always do imports first then global vars
x=0
y=0

def move(steps,op_func,z):
    if z == "x":
        global x
        x = op_func(x, int(steps))
    else:
        global y
        y = op_func(y, int(steps))

def main():

    # We only need to define this once
    directions = {"UP":partial(move, op_func=operator.add, z="x"),
                  "DOWN":partial(move, op_func=operator.sub, z="x"),
                  "RIGHT":partial(move, op_func=operator.add, z="y"),
                  "LEFT":partial(move, op_func=operator.sub, z="y")}

    user_direct=raw_input("Enter the way that you want the plane to move and how many steps you want it to move\n")
    while user_direct != " ":
        steps=user_direct[-1]
        direction=user_direct[:-1].replace(" ","")
        move_func = directions[direction]
        move_func(steps)

        user_direct=raw_input("Enter the way that you want the plane to move and how many steps you want it to move\n")

        global x
        global y
        distance=math.sqrt(x**2+y**2)
        print distance
main()

3 Comments

Thanks, but Im new to python, I didnt get this partial thing, but I sure I will use it in the future.
aaannd the correct answer is taken from me :P I definitely like this better though, use of partial is nicer
partial is a concept that comes from functional programming (hence from functools import partial): it is not often taught up front but can be extremely useful in cases like this.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.