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()
Cœur
  • 37,241
  • 25
  • 195
  • 267
Nadav Barak
  • 177
  • 1
  • 3
  • 14
  • 1
    What is `op_func` and `z`? – Tenzin Nov 20 '15 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 – R Nar Nov 20 '15 at 19:46
  • You should find your answer here http://stackoverflow.com/questions/15239783/why-reset-python-global-value-doesnt-take-effect – notorious.no Nov 20 '15 at 19:54
  • 2
    @notorious not even close – R Nar Nov 20 '15 at 19:55
  • Perhaps `op_func()` always returns 0? – twalberg Nov 20 '15 at 19:55
  • No, the problem is what @RNar said: the way the `directions` dictionary is initialized `move` is being called four times. What the OP is actually looking to do is create a set of 'partial' functions and store them in a dictionary. – Nathaniel Ford Nov 20 '15 at 20:00
  • @twalberg it return the right number but the global isn'w changing. I think @R Nar is right and it going up,down,left and right and that's why the global stays 0. but I didn't realy understand why – Nadav Barak Nov 20 '15 at 20:01

2 Answers2

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
R Nar
  • 5,465
  • 1
  • 16
  • 32
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()
Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
  • Thanks, but Im new to python, I didnt get this partial thing, but I sure I will use it in the future. – Nadav Barak Nov 20 '15 at 20:32
  • aaannd the correct answer is taken from me :P I definitely like this better though, use of `partial` is nicer – R Nar Nov 20 '15 at 20:32
  • `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. – Nathaniel Ford Nov 20 '15 at 20:33