Προσπαθώντας να λύσω μια άσκηση του τετραδίου μαθητή στην οποία ζητείται να διαχωριστεί μια λίστα αριθμών σε δυο λίστες θετικών και αρνητικών υλοποίησα την παρακάτω "αφελή" λύση:
def splitbySign(L):
positives = negatives = []
for number in L:
if number > 0 :
positives.append(number)
elif number < 0 :
negatives.append(number)
print positives
print negatives
Αν εκτελέσουμε τον παρακάτω κώδικα η απάντηση που θα πάρουμε δεν είναι σωστή.
Που είναι το λάθος? Γιατί συμβαίνει αυτό? Πως θα το διορθώσουμε χωρίς να πειράξουμε τον κώδικα μέσα στην επανάληψη?
Στο βιβλίο υπάρχει αντίστοιχο παράδειγμα το οποίο όμως είναι υλοποιημένο ως εξής και το οποίο δουλεύει σωστά!
def splitbySign2(L):
positives = negatives = []
for number in L:
if number > 0 :
positives = positives + [number]
elif number < 0 :
negatives = negatives + [number]
print positives
print negatives
Αυτό γιατί δουλεύει?
Αναφέρω το παραπάνω πρόβλημα γιατί το θεωρώ ενδιαφέρον.
Κάτι τέτοια παραδείγματα δείχνουν ότι πρέπει να επικεντρωνόμαστε στις βασικές έννοιες και όχι στην ασκησεολογία.
Ενδιαφέρoυσα σπαζοκεφαλιά για επίδειξη στο μάθημα :D
Αρχικά το λάθος βρίσκεται στο positives = negatives = [] διότι δείχνουν στην ίδια θέση μνήμης οπότε κάθε εισαγωγή ενός νέου στοιχείου αφορά και στις δύο λίστες.
---
Στη συνέχεια δεν προκύπτει λάθος γιατί η εντολή positives = positives + [number] ξαναορίζει νέα λίστα positives & negatives αντίστοιχα
μπορούν να εξηγηθούν στους μαθητές με τη βοήθεια της εντολής id, id(positives).
Να ακόμα ένα λάθος
# Δημιουργία αντιγράφου λίστας
first = [1, 2, 3]
second = first
first.append(4)
print first, second
οπότε θα προκύψει και το εύλογο ερώτημα: Πως κάνουμε αντίγραφο μίας λίστας οέο >:(
Παράθεση από: evry στις 09 Φεβ 2017, 08:22:08 ΜΜ
Προσπαθώντας να λύσω μια άσκηση του τετραδίου μαθητή στην οποία ζητείται να διαχωριστεί μια λίστα αριθμών σε δυο λίστες θετικών και αρνητικών υλοποίησα την παρακάτω "αφελή" λύση:
def splitbySign(L):
positives = negatives = []
for number in L:
if number > 0 :
positives.append(number)
elif number < 0 :
negatives.append(number)
print positives
print negatives
Αν εκτελέσουμε τον παρακάτω κώδικα η απάντηση που θα πάρουμε δεν είναι σωστή.
Που είναι το λάθος? Γιατί συμβαίνει αυτό? Πως θα το διορθώσουμε χωρίς να πειράξουμε τον κώδικα μέσα στην επανάληψη?
Στο βιβλίο υπάρχει αντίστοιχο παράδειγμα το οποίο όμως είναι υλοποιημένο ως εξής και το οποίο δουλεύει σωστά!
def splitbySign2(L):
positives = negatives = []
for number in L:
if number > 0 :
positives = positives + [number]
elif number < 0 :
negatives = negatives + [number]
print positives
print negatives
Αυτό γιατί δουλεύει?
Αναφέρω το παραπάνω πρόβλημα γιατί το θεωρώ ενδιαφέρον.
Κάτι τέτοια παραδείγματα δείχνουν ότι πρέπει να επικεντρωνόμαστε στις βασικές έννοιες και όχι στην ασκησεολογία.
Τέτοια παραδείγματα (που σημειωτέον η υλοποίηση του βιβλίου ειναι βλακώδης) δείχνουν πόσο δύσκολο (και σημαντικό) είναι να καταλάβει κανείς την διαφορά μεταξύ αντικειμένου και reference σε αντικείμενο.
Δηλαδή ότι το second = first στο παράδειγμα του taxata είναι pointer assignment και όχι object copy.
Ο δρόμος προς την τελειότητα είναι στρωμένος με λάθη ...
a = 1
b = a
b = 2
print a, b
# ---------------
a = [1]
b = a
b[0] = 2
print a, b
τι έγινε εδώ βρε παιδιά :o
και αυτό έχει ενδιαφέρον:
A = [1, 2, 3, 4]
L = [A, A, A]
L[1][1] = 100
print L
print A
Επεμβαίνω, αν και δεν διδάσκω, το μάθημα, αλλά έχω τις εξής παρατηρήσεις:
Στο αρχικό 'λάθος' του Ευριπίδη για ποιον λόγο να χρησιμοποιήσεις το ιδίωμα της διπλής ανάθεσης στο positives = negatives = []. Προσφέρει κάτι (αλγοριθμικά / εννοιολογικά) στον τρόπο σκέψης των μαθητών;
Παράθεση από: itt στις 09 Φεβ 2017, 09:39:22 ΜΜ
Τέτοια παραδείγματα (που σημειωτέον η υλοποίηση του βιβλίου ειναι βλακώδης) δείχνουν πόσο δύσκολο (και σημαντικό) είναι να καταλάβει κανείς την διαφορά μεταξύ αντικειμένου και reference σε αντικείμενο.
Επιπλέον δείχνουν και την αποτυχία πολλών σύγχρονων γλωσσών προγραμματισμούν να το κάνουν σαφές στον προγραμματιστή ;)
Η χρήση του τελεστή + για συνένωση λιστών νομίζω ότι είναι μια καλή στρατηγική για να ξεκινήσεις στις λίστες, αφού ήδη οι μαθητές έχουν δει τον ίδιο τελεστή συνένωσης στα αλφαριθμητικά. Άρα μειώνεις τον διδακτικό θόρυβο και εισάγεις κάποιες βασικές έννοιες πιο γρήγορα και εύκολα. Αφού δεις ότι έχουν καταλάβει την έννοια της λίστας τότε μπορείς να μιλήσεις για την append και να εξηγήσεις γιατί πρέπει να χρησιμοποιούν αυτήν.
Δεν χρειάζεται να δείχνουμε πάντα την καλύτερη υλοποίηση. Το σκεπτικό είναι να διευκολύνουμε τους μαθητές επειδή υπεισέρχονται πολλές νέες έννοιες.
Τώρα όσον αφορά την εντολή
positives = negatives = [ ]
πράγματι δεν υπάρχει κάποιο σκεπτικό. Μας ξέφυγε. :-[
Ωστόσο τώρα που το βλέπω είναι μια αφορμή να δώσεις στους μαθητές το τμήμα του προβληματικού κώδικα και να γίνει μια συζήτηση μέσα στην τάξη
Για να σιγουρευτώ, στην πρώτη περίπτωση έχουμε object copy και στη δεύτερη pointer assignment;
Παράθεση από: taxata στις 09 Φεβ 2017, 10:04:49 ΜΜ
a = 1
b = a
b = 2
print a, b
# ---------------
a = [1]
b = a
b[0] = 2
print a, b
Κοιτά για να το θέσουμε σωστά (δηλαδή pythonικά) το a και b, είναι 2 bindings σε ένα object.
Τα πάντα στην python είναι object (ακόμα και οι ακέραιοι στην περίπτωσή μας). Κάποια object είναι immutable όμως (όπως οι ακέραιοι).
Το a = 2, σημαίνει ότι κάνεις bind το a σε ένα int object με την τιμή 2.
To b = a σημαίνει ότι το b κάνει bind στο object που έχει γίνει bind το a.
Δεν υπάρχει κανένα copy. Μπορείς και εσύ να κάνεις verify ότι το b = a, αφού ισχύει id(b) == id(a), εαν το τρέξεις σε έναν interpreter.
Ομοίως και το δεύτερο, απλά στο δεύτερο το object είναι μία λίστα.
Συνοπτικά από τα παραδείγματα των συναδέλφων προκύπτει υπάρχει πρόβλημα ορθής κατανόησης:
1) Της σημασιολογίας της χρήσης των μεταβλητών στην python (variable semantics)
2) Της σημασιολογίας και τον μηχανισμός υλοποιήσης του περάσματος ορισμάτων σε μια συνάρτηση.
Στη wikipedia αναφέρεται και σαν Evaluation strategy (https://en.wikipedia.org/wiki/Evaluation_strategy)
Ως προς την σημασιολογία των μεταβλητών κατατοπιστικό μου φάνηκε και το: Understanding python variables (https://mathieularose.com/python-variables/)
Όπως γράφει και ο itt στην python μια εντολή ανάθεσης τιμής (assignment statement) (https://docs.python.org/2/reference/simple_stmts.html#assignment-statements)
συσχετίζει (binds) ένα όνομα με ένα αντικείμενο.
Ένα όνομα δεν μπορεί να συσχετιστεί με άλλο όνομα παρά μόνο με αντικέιμενο.
>> a = 0 # ok
>> a = b
NameError: name 'c' is not defined
Αν αντιπαραβάλεις από την μια των κώδικα του διαχωρισμού λίστας και της υλοποίησης της διεπαφής της στοιβας (την διορθωμένη) ή χρήση ή όχι της append δεν είναι εύκολη για καθηγητή πόσο μάλλον για μαθητή.
Υλοποίηση Στοίβας
def push(stack, item) :
stack.append( item )
def pop(stack) :
return stack.pop( )
def isEmpty(stack) :
return len(stack) == 0
def createStack( ) :
return [ ]
positives = negatives = [ ]
for number in numbers :
# για κάθε αριθμό της λίστας
if number > 0 :
# αν είναι θετικός
positives = positives + [ number ]
# πρόσθεσέ τον στη λίστα με τους θετικούς
else :
negatives = negatives + [ number ]
# αλλιώς στη λίστα με τους αρνητικούς
print positives
print negatives
Διαχωρισμός λίστας σε pythontutor (http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B11,-22,33,-44%5D%0Apositives%20%3D%20negatives%20%3D%20%5B%20%5D%0Afor%20number%20in%20numbers%20%3A%20%20%20%20%20%20%20%20%20%0A%20%23%20%CE%B3%CE%B9%CE%B1%20%CE%BA%CE%AC%CE%B8%CE%B5%20%CE%B1%CF%81%CE%B9%CE%B8%CE%BC%CF%8C%20%CF%84%CE%B7%CF%82%20%CE%BB%CE%AF%CF%83%CF%84%CE%B1%CF%82%0A%20%20%20if%20number%20%3E%200%20%3A%0A%20%20%20%20%20%20%20%20%23%20%CE%B1%CE%BD%20%CE%B5%CE%AF%CE%BD%CE%B1%CE%B9%20%CE%B8%CE%B5%CF%84%CE%B9%CE%BA%CF%8C%CF%82%0A%20%20%20%20%20%20%20%20positives%20%3D%20positives%20%2B%20%5B%20number%20%5D%0A%20%20%20%20%20%20%20%23%20%CF%80%CF%81%CF%8C%CF%83%CE%B8%CE%B5%CF%83%CE%AD%20%CF%84%CE%BF%CE%BD%20%CF%83%CF%84%CE%B7%20%CE%BB%CE%AF%CF%83%CF%84%CE%B1%20%CE%BC%CE%B5%20%CF%84%CE%BF%CF%85%CF%82%20%CE%B8%CE%B5%CF%84%CE%B9%CE%BA%CE%BF%CF%8D%CF%82%0A%20%20%20else%20%3A%0A%20%20%20%20%20%20%20negatives%20%3D%20negatives%20%2B%20%5B%20number%20%5D%0A%23%20%CE%B1%CE%BB%CE%BB%CE%B9%CF%8E%CF%82%20%CF%83%CF%84%CE%B7%20%CE%BB%CE%AF%CF%83%CF%84%CE%B1%20%CE%BC%CE%B5%20%CF%84%CE%BF%CF%85%CF%82%20%CE%B1%CF%81%CE%BD%CE%B7%CF%84%CE%B9%CE%BA%CE%BF%CF%8D%CF%82%0Aprint%20positives%0Aprint%20negatives&cumulative=false&curInstr=0&heapPrimitives=false&mode=display&origin=opt-frontend.js&py=2&rawInputLstJSON=%5B%5D&textReferences=false)
Ο Διαχωρισμός λίστας με την append που δεν λειτουργεί στο pythontutor (http://pythontutor.com/visualize.html#code=lista%20%3D%20%5B11,-22,33,-44%5D%0Apositives%20%3D%20negatives%20%3D%20%5B%5D%0Afor%20number%20in%20lista%3A%0A%20%20%20%20if%20number%20%3E%200%20%3A%0A%20%20%20%20%20%20%20%20positives.append%28number%29%0A%20%20%20%20elif%20number%20%3C%200%20%3A%0A%20%20%20%20%20%20%20%20negatives.append%28number%29%0Aprint%20positives%0Aprint%20negatives%0A%20&cumulative=false&curInstr=0&heapPrimitives=false&mode=display&origin=opt-frontend.js&py=2&rawInputLstJSON=%5B%5D&textReferences=false)
Ενδιαφέρον και κατατοπιστικό στο θέμα είναι και το Is Python pass-by-reference or pass-by-value? (http://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/)
Λίγο αργά βρήκα και τη σχετική συζήτηση σε άλλο νήμα : Διορθώσεις Βιβλίου Προγραμματισμός Υπολογιστών Γ ΕΠΑΛ (https://alkisg.mysch.gr/steki/index.php?topic=6911.0)
def test(mylist):
mylist[0] = mylist[1] = 100
>>> a = [2, 4, 5]
>>> test( a )
>>> print a
[100,100,5]
Αυτό το παράδειγμα του evry δείχνει ότι δεν εχουμε call by value .
Για το παράδειγμα του taxata (πως δημιουργούμε αντίγραφο λίστας;
>> # Δημιουργία αντιγράφου λίστας
>>first = [1, 2, 3]
>> second = first
>> first.append(4)
>> print first, second
[1,2,3,4],[1,2,3,4]
η απάντηση είναι στην δραστηριότητα 5.3.4 του βιβλίου της Β :
>>> a = [5,8,13,21,34]
>>> b = a[:]
>>> d = a
>>> a.pop()
34
>>> d.pop()
21
>>> a[0]=a[1]=6
>>> print a,b,d
[6, 6, 13] [5, 8, 13, 21, 34] [6, 6, 13]
>>> # η λίστα b δεν άλλαξε
Διαδραστική αποσφαλμάτωση του παραπάνω κώδικα (http://pythontutor.com/visualize.html#code=a%20%3D%20%5B5,8,13,21,43%5D%0Ab%20%3D%20a%5B%3A%5D%0Ad%20%3D%20a%0Aa.pop%28%29%0Ad.pop%28%29%0Aa%5B0%5D%3Da%5B1%5D%3D6%0Aprint%20a,b,d&cumulative=false&curInstr=0&heapPrimitives=false&mode=display&origin=opt-frontend.js&py=2&rawInputLstJSON=%5B%5D&textReferences=false)στο pythontutor.com
Local variable referenced before assignment in Python? (http://stackoverflow.com/questions/18002794/local-variable-referenced-before-assignment-in-python) Μια ακόμη εξήγηση για το παράδειγμα του msoukara :
x = 50
def func():
print('Το x είναι', x)
x = 2
print('Το τοπικό x άλλαξε σε', x)
func()
print('Το x είναι ακόμα', x)
# UnboundLocalError: local variable 'x' referenced before assignment
ΥΓ: μεταφέρω-αντιγραφω σκόρπια παραδειγματα προκειμένου ισως να φανεί καλύτερα το ζητούμενο
Επειδή ειδικά τα τελευταία μηνύματα δεν είχαν απολύτως καμία σχέση με το θέμα του thread.
Στο παρακάτω thread μεταφέρθηκαν τα μηνύματα σχετικά με το πόσο ακατάλληλη είναι η Python για την εκπαίδευση:
Είναι η Python Εκπαιδευτική γλώσσα? (https://alkisg.mysch.gr/steki/index.php?topic=7027.msg79664#msg79664)
και στο παρακάτω μπορείτε να γράψετε ότι θέλετε περί φιλοσοφίας, μαθηματικών, πληροφορικής κλπ
Δείκτες, Αριθμητική Peano, Φιλοσοφία κλπ (https://alkisg.mysch.gr/steki/index.php?topic=7026.msg79669#msg79669)
Υπάρχει πάντως και η απόδειξη του Godel για την ύπαρξη του Θεού που ταιριάζει αρκετά θα έλεγα στο παραπάνω thread!
Gödel's ontological proof (https://en.wikipedia.org/wiki/G%C3%B6del's_ontological_proof)
αφού συνδυάζει μαθηματικά, φιλοσοφία και ... πληροφορική
Automating Godel's Ontological Proof of God's Existence with Higher-order Automated Theorem Provers (http://page.mi.fu-berlin.de/cbenzmueller/papers/C40.pdf)
Παρακαλώ στο thread αυτό μόνο μηνύματα σχετικά με το αρχικό πρόβλημα που έθεσα και είναι πολύ σημαντικό στην διδακτική πράξη. :police: