Talk About Quality

Tom Harris

Archive for the ‘Software’ Category

Computer Programming – The Rules of the Game

with one comment

A basis of a vector space is a set of vectors in that space that can be used as coordinates for it. [1]

I just read Dan Meyer‘s quick review of the Graspable Math tool. While I did briefly investigate Graspable Math, and also glanced at Desmos, the tool where Dan is head of teaching, what really struck me was how Dan’s writing shows him to be “on the inside” of math teaching and learning. As I am too. That is, there’s a room called “math”, both of us are inside it, and we’re familiar with the rules of the game.

As I love computer programming and am always looking for new ways to teach it, the question arose in my mind: what is the minimal set of constructs to teach students so that they can take off from there and learn the rest of computer programming, in any language, pretty much by themselves?

I did a quick search for “minimal constructs of programming” and found Software Design & Development: Programming Constructs (pdf) from The High School of Dundee in Scotland. Nice and clear, but according to the table of contents, roughly 30 topics. That feels like too many to be minimal.

When faced with the question of how to teach, I usually first turn back to myself – how do I learn? That answer is not likely to be the best or complete answer, because most students are not me. (A valuable lesson for teachers, that I struggle to keep in mind!) But it’s an OK start, and often it’s all I have.

When I learn a new programming language, what constructs do I look for?

  • Variables (scalars, vectors; and assignment – same “=” sign means something very different from math)
  • Operators (may look very different in, say, Python vs APL vs AWK)
  • Conditionals
  • Loops
  • Input/Output
  • Functions (could be an intermediate level topic, but so important for clear code that I put it here)

Maybe also

  • Imports (to get access to additional commands)
  • Classes (for object-oriented programming — question whether that’s an essential or advanced topic)

And there’s also environment and tools:

  • How to edit
  • How to compile (if necessary)
  • How to run (both in editor and from command line — so “command line” is a concept too)
  • How to interact with the program (where do I put input? where do I see output?)

The absolute minimum count of the above is 10 topics. Is that the minimum? Is that the right set? And, is it a set of topics where each one makes sense to a student who is new to computer programming? That last one is so important because (as above), my students are not me.

Thus, towards a second draft of the list, and maybe some hints to what kinds of explanations, exercises, and even learning-support tools (like the math environments mentioned at the beginning of this post, only for computer programming), I want to consider the question in some new ways. Based less on how I learn computer programming, and more on how people, even children, learn things from each other:

If computer programming were an outdoor game played at recess, and I were ten years old, how would I teach my friends the rules of the game?

 

If computer programming were a dance, what would be the basic steps?

What are your answers to these questions?

 

Advertisement

Written by Tom Harris

June 12, 2018 at 12:06 pm

Learning a New Programming Language, with Life (part 4)

leave a comment »

While preparing a course syllabus on coding in Python, for an upcoming high school class, I remembered static analysis. I haven’t decided how soon to introduce the topic, but I thought I’d better check how my own sample program fared.

No warnings or errors from Python itself. (If there had been I would have addressed them already!).

So I installed Pylint and tried its default settings. How about that. Not zero at all!

In fact, 54 coding convention messages, 12 warnings, and 1 recommendation for refactoring.

It was easy enough to clean up whitespace issues (helped to turn on “view whitespace” in my Notepad2).

And yes, many comment lines were too long. I left-justified (but indented for Python) for easy reading.

Some of the warnings are for my “TODO” comments — an extra reminder to do, or drop, next steps I’d identified earlier. Pylint message count is now down to 2 informational messages, 28 coding convention, 13 warnings, and 1 to refactor. They are valuable for my future Python learning (and teaching):

Locally disabling unused-variable (W0612) (locally-disabled)

I disabled those warnings because I know the code needs those variables. But I’ll have to explain why.

(Pylint doesn’t forget — the 2 new informational messages remind that I’ve suppressed two warnings.)

Invalid attribute name “maxRowMain” (invalid-name)

Not just that name. Most of my object names. I’ll have to find a good object-naming convention and use it.

R: 69, 4: Too many branches (13/12) (too-many-branches)

Just today I heard a Python lecturer on YouTube say, “if you’re not refactoring, you’re not learning.” Yes, that function is the longest. Not so complex, but could be simpler and easier to understand.

Attribute ‘maxRowMain’ defined outside __init__ (attribute-defined-outside-init)

A few of those also. I will have to go back and learn again about __init__: when to use it and why.

All in all, a good learning session, and direction on what I need to learn next.

Thanks to static analysis with Pylint.

Written by Tom Harris

September 2, 2015 at 10:30 pm

Learning a New Programming Language, with Life (part 3)

with one comment

I started learning Python three days ago, as described here in part 1 and part 2.

Just two hours a day, and I’m well on my way, because here below is a working implementation of my favorite, Conway’s Game of Life, in Python.

Today I refactored to put instance-specific initializations right in the functions that needed them, and promoted parsing of command line arguments to the main program. (Future: use argparse.)

I also realized that these functions are just procedures, so I stopped trying to return meaningful values. I removed the return statements. Python supports that, and returns None.

Update:

A day later, after questions from a reader led me to review my own code, I wondered why it works even without wraparound for negative indices!

The answer: Python lists handle negative indexes gracefully.

Let’s go to the code

#Life.py

#Implementation of Conway’s Game of Life
#See https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

#Tom Harris 22-Jul-2015
#Tom Harris 23-Jul-2015 add multiple generations and display period to main program

#Import libraries needed
import sys #for command line argument support

#Define the Life class

class Life:
….#class attributes
….#None

….#class methods
….#NOTE: Currently all procedures — no return statement, implicitly returns None
….
….def readBoardFromFile(self, filename):
……..#Initialize (clear) the board of Life cells
……..self.mainBoard = []

……..#Open the file
……..initialGenerationFile = open(filename)

……..#Read from the file, line by line:
……..for line in initialGenerationFile:
…………listLine = list(line.rstrip(‘\r\n’))
…………self.mainBoard.append(listLine) #add the line, in Python list format, to the mainBoard

……..#Determine dimensions of the board after reading in
……..#WARNING: dimensions are 1 larger than largest index, since indices start at 0
……..self.maxRowMain = len(self.mainBoard)
……..self.maxColMain = len(self.mainBoard[0])

……..#Close the file
……..initialGenerationFile.close

….def displayBoard(self):
……..for rowIndex, row in enumerate(self.mainBoard):

…………for colIndex, col in enumerate(row):
…………….if self.mainBoard[rowIndex][colIndex]== “1”:
………………..print(“X”, end=”)
…………….else:
………………..print(“.”, end=”)

…………print() #print newline at end of each row

….def makeBoardCurrent(self): #TODO: Think of a better name — means to copy from the next-generation workingBoard to mainBoard
……..self.mainBoard = self.workingBoard
……..
….def calculateNextGeneration(self):
……..#Calculate next generation on workingBoard based on mainBoard

……..#Initialize work area
……..self.workingBoard = [] #A work area for creating the next Life generation

……..#Calculate next generation on workingBoard based on mainBoard
……..for mainRowIndex, mainRow in enumerate(self.mainBoard):

…………#Initialize working row before using for each row
…………workingRow = [] #for building up workingBoard row by row

…………for mainColIndex, mainElement in enumerate(mainRow):

…………….#Restart count of live cells
…………….numLiveCells = 0

…………….for i in [-1, 0, 1]:
………………..#Check row before, same row, next row
………………..rowOfCellToCheck = mainRowIndex + i

………………..#Wraparound
………………..if rowOfCellToCheck (self.maxRowMain – 1):
……………………rowOfCellToCheck = 0
……………………
………………..for j in [-1, 0, 1]:
……………………#Check column before, same column, next column
……………………colOfCellToCheck = mainColIndex + j

……………………#Wraparound
……………………if colOfCellToCheck (self.maxColMain – 1):
……………………….colOfCellToCheck = 0

……………………#If cell to check is alive, increment count of live cells
……………………#But if same row and column, ignore
……………………sameColAndRow = abs(i) + abs(j)
……………………if sameColAndRow != 0:
……………………….if self.mainBoard[rowOfCellToCheck][colOfCellToCheck] == “1”:
………………………. numLiveCells = numLiveCells + 1
…………………………………………

…………….if self.mainBoard[mainRowIndex][mainColIndex] == “1”: # The survival rules

………………..if 2 <= numLiveCells <= 3:
……………………workingRow.append(‘1’)
………………..else:
……………………workingRow.append(‘0’)

…………….else: #The reproduction rule

………………..if numLiveCells == 3:
……………………workingRow.append(‘1’)
………………..else:
……………………workingRow.append(‘0’)
……………………
…………self.workingBoard.append(workingRow)
…………………………..
#The actual main Life program
#Usage will be Life(filename, generations to calculate, every how many generations to display, display yes/no, write to files yes/no)
#First Usage Life() — done 22-Jul 10:24
#Second Usage Life(filename) — reading in file done 22-Jul-2015 10:59
#Third Usage Life(filename) — including displaying the file and dummy next generation 22-Jul-2015 18:34
#Fourth Usage Life(filename) — real next generation done 22-Jul-2015 20:17
#Fifth Usage Life(filename, generations to calculate, every how many generations to display) done 23-Jul-2015 20:44
#NOTE: Decided not to implement writing single generations to separate files. Seems unnecessary and would just fill my disk with lots of files.
#NOTE: Piping console output to text file is fine. Modern text editors can open large, multi-generation Life files and navigate quickly
#TODO: Next learning topics (backlog) would be:
#….1. Graphic display
#….2. Mouse input of boards
#….3. Change to argparse for robust command line parsing
#….4. Clean up file layout according to some accepted Python style guide
#….5. Refactor to be more functional as opposed to procedural, and reduce global variable use, if that makes code clearer

#Create a single Life instance
myLife = Life()

#Get the command line arguments
#TODO:Switch to Python argparse module instead

#From first command line argument, get pathname of initial (zero’th) generation text file
initialGenerationFilename = sys.argv[1]

#From second command line argument, get number of generations to calculate
numGenerations = int(sys.argv[2])

#From third command line argument, get period: every how many generations to display or write to file
#NOTE: currently, writing results to a file is not yet included
periodOfDisplay = int(sys.argv[3])

#Read in the initial (zero’th) generation board from text file
myLife.readBoardFromFile(initialGenerationFilename)

#Display initial (zero’th) generation
print(“Generation #”,0)
myLife.displayBoard()
print()

#Generate and display following generations
for generationIndex in range(1,numGenerations+1): #NOTE:Add 1 because range end is always one less than value
….myLife.calculateNextGeneration()
….myLife.makeBoardCurrent()
….if (generationIndex % periodOfDisplay) == 0: #NOTE: “%” is Python modulus — checking for evening divisible by period of display
……..print(“Generation #”,generationIndex)
……..myLife.displayBoard()
……..print()

And here’s some output. The 4-generation-period “glider” on a 25 x 25 grid, starts its flight from upper-left towards lower-right.
The command I ran was

Life.py Glider25by25.txt 8 4

and the output was

Generation # 0
..X......................
...X.....................
.XXX.....................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................

Generation # 4
.........................
...X.....................
....X....................
..XXX....................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................

Generation # 8
.........................
.........................
....X....................
.....X...................
...XXX...................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................
.........................

Written by Tom Harris

July 24, 2015 at 12:48 am

Learning a New Programming Language, with Life (part 2)

with one comment

I started learning Python two days ago, as described in part 1.

Yesterday I learned about Python Classes and Functions, including class and instance variables, and the special __init__ function. Today I learned enumerate, and the list function rstrip. I also learned not to forget the colon (:) in def, for, if, and else constructs!

After just a few hours today, my first project has actually started working.

For one “generation”, according to the rules of Conway’s Game of Life.

Here it is, complete with TEST comments preceding debugging code, and TODO comments for what’s next.

#Life.py

#Implementation of Conway's Game of Life
#See https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

#Tom Harris 22-Jul-2015

#Import libraries needed
import sys #for command line argument support

#Global functions
def initialGenerationFilename():
….#Get pathname from first command line argument
….return sys.argv[1]

#Define the Life class

class Life:
….#class attributes
….#None

….#class methods

….def __init__(self):
……..#instance attributes: define and initialize (some more later after reading in file)
……..self.mainBoard = [] #The board of cells
……..self.workingBoard = [] #A work area for creating the next Life generation
….
….def readBoardFromFile(self, filename):
……..#Open the file
……..initialGenerationFile = open(filename)

……..#Read from the file, line by line:
……..for line in initialGenerationFile:
…………listLine = list(line.rstrip(‘\r\n’))
…………self.mainBoard.append(listLine) #add the line, in Python list format, to the mainBoard

……..#Determine dimensions of the board after reading in
……..#WARNING: dimensions are 1 larger than largest index, since indices start at 0
……..self.maxRowMain = len(self.mainBoard)
……..self.maxColMain = len(self.mainBoard[0])

……..#Close the file
……..initialGenerationFile.close

……..return self.mainBoard

….def displayBoard(self):
……..for rowIndex, row in enumerate(self.mainBoard):

…………for colIndex, col in enumerate(row):
…………….if self.mainBoard[rowIndex][colIndex]== “1”:
………………..print(“*”, end=”)
…………….else:
………………..print(“.”, end=”)

…………print() #print newline at end of each row

……..return 0 #TODO: replace with returning a result — what do we do with it? This is a function, not a procedure.

….def makeBoardCurrent(self): #TODO: Think of a better name — means to copy from the next-generation workingBoard to mainBoard
……..self.mainBoard = self.workingBoard
……..
……..return self.mainBoard

….def calculateNextGeneration(self):

……..#Calculate next generation on workingBoard based on mainBoard
……..workingRow = [] #for building up workingBoard row by row

……..#Calculate next generation on workingBoard based on mainBoard
……..for mainRowIndex, mainRow in enumerate(self.mainBoard):
…………for mainColIndex, mainElement in enumerate(mainRow):

…………….#Restart count of live cells
…………….numLiveCells = 0

…………….for i in [-1, 0, 1]:
………………..#Check row before, same row, next row
………………..rowOfCellToCheck = mainRowIndex + i

………………..#Wraparound
………………..if rowOfCellToCheck < 0:
……………………rowOfCellToCheck = self.maxRowMain – 1

………………..if rowOfCellToCheck > (self.maxRowMain – 1):
……………………rowOfCellToCheck = 0
……………………
………………..for j in [-1, 0, 1]:
……………………#Check column before, same column, next column
……………………colOfCellToCheck = mainColIndex + j

……………………#Wraparound
……………………if colOfCellToCheck < 0:
……………………….colOfCellToCheck = self.maxColMain – 1

……………………if colOfCellToCheck > (self.maxColMain -1):
……………………….colOfCellToCheck = 0

……………………#If cell to check is alive, increment count of live cells
……………………#But if same row and column, ignore
……………………sameColAndRow = abs(i) + abs(j)
……………………if sameColAndRow != 0:
……………………….if self.mainBoard[rowOfCellToCheck][colOfCellToCheck] == “1”:
………………………. numLiveCells = numLiveCells + 1
…………………………………………

…………….if self.mainBoard[mainRowIndex][mainColIndex] == “1”: # The survival rules

………………..if 2 <= numLiveCells <= 3:
……………………workingRow.append(‘1’)
………………..else:
……………………workingRow.append(‘0’)

…………….else: #The reproduction rule

………………..if numLiveCells == 3:
……………………workingRow.append(‘1’)
………………..else:
……………………workingRow.append(‘0’)
……………………
…………self.workingBoard.append(workingRow)
…………workingRow = []
………………..
……..return self.workingBoard

#The actual main Life program
#Usage will be Life(filename, generations to calculate, every how many generations to display, display yes/no, write to files yes/no)
#First Usage Life() — done 22-Jul 10:24
#Second Usage Life(filename) — reading in file done 22-Jul-2015 10:59
#Third Usage Life(filename) — including displaying the file and dummy next generation 22-Jul-2015 18:34
#Fourth Usage Life(filename) — real next generation done 22-Jul-2015 20:17
#TODO: Fifth Usage Life(filename, generations to calculate, every how many generations to display)

#Create a single Life instance
myLife = Life()

#Run the methods in the right order
myLife.readBoardFromFile(initialGenerationFilename())
myLife.displayBoard() #TEST: display the mainBoard before generating
print()
myLife.calculateNextGeneration()
myLife.makeBoardCurrent()
myLife.displayBoard()

And here’s the output. It’s the simplest oscillator — the “Blinker”, one generation, with wraparound:

........
*.......
*.......
*.......
........
........
........
........
........

........
........
**.....*
........
........
........
........
........
........

Written by Tom Harris

July 22, 2015 at 8:48 pm

Learning a New Programming Language, with Life

with 2 comments

For an upcoming course to help science students master the computer as a tool for their work, by learning to code, I need to learn Python. I reviewed discussions on Python 2 vs Python 3, and chose the latter.

To operate in a new language, I only need to learn and practice four things:

  1. Input
  2. Storage and Retrieval
  3. Processing
  4. Output

(Wish I could program functionally, and avoid #2. But Python isn’t designed for functional programming.)

My traditional project for learning a new language is to code Conway’s Game of Life.

After my first 2 hours in Python, with Python 3.4.3 installed, and Google for answering my questions, I’ve covered command line arguments (though still without argparse for user error handling), input from text file, output to console, line-by-line storage, and character-by-character retrieval from a Python list.

I’ve also imported one Python module (sys), and gotten used to code blocks via indentation.

Here’s my code so far. Wish me luck as I continue learning!

#ReadTextFromFileAndDisplay.py

#Import libraries needed
import sys #for command line argument support

#Get pathname of file to open from first command line argument
filenameToOpen = sys.argv[1]

#Open the file
myFile = open(filenameToOpen)
print("Pathname was: ", filenameToOpen)
print("Result code for opening the file was: \n", myFile)

#Read and display entire file
#Also store it in a list

myList = [] #declare list, initially empty

print("\nHere is the file as it's being read in: \n")
#Read from the file, line by line:
for line in myFile:
....print(line, end='') #display each line
....myList.append(line) #add the line to the list

#Close the file
myFile.close

print("\nFile should be closed now.\n")

print("Here's the contents of the list... \n")

#Display the contents of the list
for row in myList:
....print(row, end='') #entire rows
....print()
....for iCharacterIndex in range(len(row)):
........print(row[iCharacterIndex]) #character by character

p.s. I’ve put periods in to show the indentation, since it’s hard to get WordPress to preserve whitespace.

Written by Tom Harris

July 20, 2015 at 7:44 pm

Software is Writing

with one comment

Software is not construction

Software is not construction, no matter how popular that statement may be. A program is not a building. Parts are not chosen first and then fitted into place. Design continues almost to the last minutes of coding. Further, almost nobody spends time creating new code, at least not for very long. Sure there are new products and new codebases. But even then, after a short period, most additional coding is extending or changing an existing codebase. So if coding is not construction, and mostly involves changing what’s already created, what is it?

Well, coding is writing. It’s using text to communicate a message. That may not sound like much, but understanding this helps a lot. Instead of struggling to make up new ways of thinking and learning, as if programming were a completely new human activity, we can look at how good writers write. People have been writing for many hundreds of years, and have become pretty good at it.

Learning to write

How do people get into writing? Do we start, as most do in programming, full grown, writing essays? No, not at all. We start as children, and learn first to talk, then to read, and finally to write. Yes, I know that school seems to teach writing, at least forming the letters, before reading. But it’s an illusion: listening to a bedtime story is essentially reading – paying attention to someone else’s writing, and understanding it – and comes before writing. Most people don’t try writing that communicates a message – expository prose – until secondary school or later.

Good writers have their audience in mind at all times. They edit their writing through several drafts, as well as getting others to review it, and give written comments. And they talk about their writing. Maybe not always directly, but when a political columnist goes to a dinner party, you can bet he’ll talk politics. When a novelist chats in the park, she’s talking about the same human struggles that will later appear in her books. Even for the solitary writer typing in a darkened room, the life that feeds the writing is one of talking with, listening to, and reading about people.

Software is Writing

Now back to software: software is not like writing, it is writing. In our industrial setting, it is too late or too radical to suggest re-teaching programming, by teaching first how to talk, then to read, then to write programs. But we can highlight where daily programming work already involves each of these three activities, and where it doesn’t, but should.

Talking includes formal design reviews and code reviews, but also whiteboard conversations and hallway arguments. How should I write this here? Why is that data structure that way? Why does the compiler give that warning? Don’t just think alone – talk it out with someone nearby. It is the same with tests. Why does that scenario fail intermittently? How should I write it differently? And even for defect entries -what does the submitter really mean? Phone him up. Why does this change fix the problem? Find the developer and ask.

Reading is for design documents, training materials and perhaps professional books. But mainly, it includes reading lots of existing code. That’s reading it until you’re really sure what it does, so you know how to change it, fix it, extend it. There is also code review — where the author asks you to read their code, not just to say that it looks fine, but to identify what’s wrong, or unclear.

Writing is the coding, of course. But who is the audience? There are two audiences. One, the compiler, which is your personal translator, from a non-native language (nobody’s mother spoke to them in C), to an almost unknown language – the machine language that the hardware actually understands. The compiler is a rigid, simple-minded, sometimes unpredictable audience, just a ghost of the person who wrote it. Here you have to be very precise, careful, and limit yourself to what the compiler understands and to what the hardware will accept. The other audience, is so very different – the next developer. This may be yourself a few weeks later, or someone else, but there will always be another developer, coming back months or years later, to fix or extend the code. She wants comments, explanations, meaningful names for things, and an easily readable structure. Because now you’re long gone, she can’t talk to you: she must read, so you must write.

Tools!

Well, with all this talk about programming being writing, let’s come back to earth. Software developers are technical people, and expect to use the best tools to do their jobs. There’s the whiteboard in the hallway — we need more of those, for talking. Defect tracking systems, where we can make sure we write clear resolution notes. And for automatically sharing and discussing code, web-hosted code tools for static analysis and code review are the way to go.


Originally published in company internal newsletter. Edited and released here with permission.

Written by Tom Harris

September 15, 2010 at 1:51 am

Posted in Software, Writing