5 ms·
Comment on the functional programming and mutation of data. I learnt R before learnt python. In R y = c(1, 2, 3) x = y # now x is a copy of y I was surpris
by nnm 4y ago
Comment on the functional programming and mutation of data. I learnt R before learnt python.
In R
y = c(1, 2, 3)
x = y
# now x is a copy of y
I was surprised that in python
y = [1, 2, 3]
x = y
# now x is y! What, why?
In R, I am used to the fact everything is an expression:
a = if (x > y) { 0 } else { 1 }
- fspeech 4y agoBecause it is expensive to copy a large container? Which is why you want immutable data structures, in which case copying is superfluous. So the problem with Python is not the aliasing in assignment, rather it is the mutability of the objects that could create unpleasant surprises.
- fny 4y agoR has call-by-value semantics. Python has call-by-reference. These are entirely two different programming paradigms. https://stackoverflow.com/questions/15759117/what-exactly-is-copy-on-modify-semantics-in-r-and-where-is-the-canonical-source https://stackoverflow.com/questions/15759117/what-exactly-is...
- moonchild 4y agoPython is not call-by-reference. If it were, we would expect 2 to be printed below, but it is not: >>> def f(x): ... x = 2 >>> x = 1 >>> f(x) >>> x 1 One might refer to r as being referentially transparent, or immutable (I don't know if it is--I am assuming). I would refer to python as using uniform reference semantics - http://metamodular.com/common-lisp-semantics.html http://metamodular.com/common-lisp-semantics.html
- kazinator 4y agoWhat does that Python mean, by the way? The passing of the value into the function is clear enough, but I have no idea whether f is assigning to the local x, or binding a new one. If we capture a lambda before "x = 2", and then call it afterward, does it see an x which holds the original argument value or which holds 2? In Lisps, this stuff is clear. Mostly. Except Common Lisp has some areas where it waffles: in some iteration constructs, implementations can bind a new variable or reuse the same one. Those choices are individually clear, though.
- Jtsummers 4y agoCreating a new one. It isn't a closure, so: x = 3 def f(x): # note the name here x = 20 f(x) x # => 3 Similarly, using a different parameter name: def g(y): x = y g(20) x # => 3 You have to explicitly mark the `x` in the function to be the global one to reference it: def h(y): global x x = y h(10) x # => 10 With a lambda it would be a closure: i = lambda y : x # throwing away y for fun i(3) # => 10 x = 20 i("hi") # => 20
- fny 4y ago`f` behaves just like closure. It can even be assigned to a variable. def f(x): x = 2 # <- this `x` is the same `x` as in the argument, it can be access via locals() internally You can even assign `f`. For example, `function = f`. Python is call by reference. Change my mind. `def f(x): x[0] = 1` will manipulate whatever object you pass to it.
- Jtsummers 4y agoIf Python were fully call by reference then: def g(y): y = 3 x = 10 g(x) x # => ?? If it's 3 then it's pass by reference here, if it's 10 it's pass by reference. Which is it? Additionally, for something to be a closure it has to close over something (an environment). What does `f` or `g` close over? Note that they aren't changing any environment, they are "merely" functions, not closures. Python does have closures, but those aren't examples of them. And being able to assign a function to a variable does not make a closure, or do you think that C has closures because it has function pointers?
- Jtsummers 4y agoOops: If it's 3 then it's pass by reference here, if it's 10 it's pass by value. Which is it? (Don't hit reply when you're rushing out the door.)
- kazinator 4y ago> Python is call by reference. Change my mind. No need to change your entire mind; just an incorrect definition of call-by-reference. Passing a value which has reference semantics isn't call-by-reference. Your f receives x by value. That value is a reference into a boxed object. The x[0] = 1 does not change x; it changes the boxed object. >>> def f(x): ... x[0] = 1 ... >>> a = [ 0, 2, 3 ] >>> b = a >>> f(a) >>> a is b True The f function can do nothing to make "a is b" false. Under pass-by-reference, assigning to x would do that. Under pass-by-reference, we can pass a reference to a, such that a can be replaced, without affecting b. "pass-by {whatever}" refers to the semantics of the parameter and what it receives from the argument expression, and how, not the semantics of the argument object/value.
- fny 4y agoI don't expect 2 to be printed. I think you're conflating scope with calling conventions. x is locally scoped to the function. `f` behaves like a lambda/closure. Would you expect `(define f (lambda(x) x))` to have awareness about some global `x`? The only difference with Python is that the closure has access to the global scope unless the variable is locally defined and a new reference is created in the frame.
- hajile 4y agoYou're trying to demonstrate dynamic scoping. Python has lexical scoping and neither set of scoping rules is directly related to passing by reference or value.
- Jtsummers 4y agoNope, that code actually does demonstrate the difference between pass by reference and pass by value. If you prefer, change either the `x`'s in the function to be `y` or the global `x`'s to by `y` and you'll see the same behavior. Which shows that Python is not, in fact, pass by reference.
- hajile 4y agoI think I see what you are saying (the shared variable threw me), but it STILL isn't doing what you think. def s(x): x[1] = 2222 return x x = [1,2,3] print(s(x)) # prints [1, 2222, 3) As you can see, it is pass by reference. Tuples, strings, numbers, and some other things are immutable. When you pass them, you pass a reference (from the user's perspective as this can be optimized to pass by value as the interpreter sees fit). When you update that reference, because they are immutable, you must update the current reference to a new location in memory. Because you are changing what the new pointer variable in the function points to, of course the other pointer doesn't update.
- Jtsummers 4y ago> As you can see, it is pass by reference. No, it's passing a reference, but it is still not pass by reference, this is actually worth distinguishing because they are different behaviors. That function (by moonchild) does exactly what moonchild meant it to do, which was to demonstrate that Python is not pass by reference. Another function demonstrating that Python is not pass by reference: def swap(a, b): a, b = b, a a = [1,2] b = [3,4] print(a,b) swap(a,b) print(a,b) If Python were pass by reference (versus passing a reference in these cases) then it would perform a swap, but it does not perform a swap because it is not pass by reference. C++ has pass by reference (as an option), as do Pascal and Ada, and more languages. But Python is not among them. An actual pass-by-reference swap, in C++: template<typename T> // template to be the most equivalent to the intended Python code void swap(T& a, T& b) { T t = a; a = b; b = a; } That will actually perform a swap when called with `swap(x,y)`. Any language that offers (by option or by default) pass by reference can have an equivalent swap function, including a generic one like the above if the language offers some notion of generics or is dynamically typed (like Python).