Back

Activity 1.19: How Long, and What Is Not There

divider

Activity 1.19

How Long, and What Is Not There

Where We Left Off

Last session you asked for xs[0] and got nil.

The program did not crash. It carried on.

That is the most dangerous thing Lua does to you all year.

Key Concepts

How Many Are In There

Nothing Is Not an Error

Two Names, One Table

How Many?

local scores = {90, 85, 78}
print(#scores)

How Many?

local scores = {90, 85, 78}
print(#scores)
Terminal window
3

# means how many.

The Last One, Without Counting

local scores = {90, 85, 78}
print(scores[#scores])

The Last One, Without Counting

local scores = {90, 85, 78}
print(scores[#scores])
Terminal window
78

Three elements, so the last index is 3. Works for a table of a hundred without changing a character.

Asking for What Is Not There

local scores = {90, 85, 78}
print(scores[4])

Asking for What Is Not There

local scores = {90, 85, 78}
print(scores[4])
Terminal window
nil

No error. No warning. The program does not care.

So Where Does It Go Wrong?

local scores = {90, 85, 78}
local fourth = scores[4]
print("Fourth score: " .. fourth)

So Where Does It Go Wrong?

local scores = {90, 85, 78}
local fourth = scores[4]
print("Fourth score: " .. fourth)
Terminal window
Error: main.lua:4: attempt to concatenate local 'fourth' (a nil value)
main.lua:4: in main chunk

The error says line 4. The mistake is on line 2.

This Is the Whole Problem

nil arrives quietly and breaks something later, somewhere else.

The line in the error message is where it was used, not where it came from.

Two Names, One Table

local scores = {90, 85, 78}
local backup = scores
backup[1] = 0
print("backup[1] is " .. backup[1])
print("scores[1] is " .. scores[1])

Two Names, One Table

local scores = {90, 85, 78}
local backup = scores
backup[1] = 0
print("backup[1] is " .. backup[1])
print("scores[1] is " .. scores[1])
Terminal window
backup[1] is 0
scores[1] is 0

It is called backup and it is not a backup.

Today's Objectives

  • Get the number of elements with #
  • Read the last element without counting
  • Check for nil before using a value
  • Explain why one table with two names is not two tables

Key Terms

nil
Lua's way of saying nothing is here.
Length
How many elements a table holds, written #t.
Out of range
An index the table has no element for.
Alias
A second name for the same table, not a copy of it.

'F' → Fullscreen

divider

Build

No window today — everything here is easier to see as text. Copy template-console, rename it 1-19-lists, and work in main.lua.

Task 1: How Many, and the Last One

main.lua
local scores = {90, 85, 78}
print("There are " .. #scores .. " scores")
print("The last one is " .. scores[#scores])
Terminal
There are 3 scores
The last one is 78

scores[#scores] looks strange and reads simply: the element at the position "how many there are." Add a fourth score to the table and run it again without changing anything else. Both lines follow along.

Task 2: Break It on Purpose, Then Fix It

First, make it fail. Type this and run it:

main.lua
local scores = {90, 85, 78}
local fourth = scores[4]
print("Fourth score: " .. fourth)
Terminal
Error: main.lua:4: attempt to concatenate local 'fourth' (a nil value)
main.lua:4: in main chunk

Write down which line the error names, and which line the mistake is on. They are not the same line, and that is the point of the whole session.

Now fix it, by checking before you use it:

main.lua
local scores = {90, 85, 78}
local wanted = 4
if scores[wanted] == nil then
print("There is no score number " .. wanted)
else
print("Score number " .. wanted .. " is " .. scores[wanted])
end
Terminal
There is no score number 4

Change wanted to 2 and run it again. The same program now takes the other branch.

Task 3: The Backup That Is Not a Backup

Predict first, on your sheet. Then run it.

main.lua
local scores = {90, 85, 78}
local backup = scores
backup[1] = 0
print("backup[1] is " .. backup[1])
print("scores[1] is " .. scores[1])

Write down what you expected and what happened. If you were surprised, say so — that is worth marks and it is worth more than being right.

Task 4: Make a Real Copy

One table cannot become two by giving it another name. To get a real copy you have to move the elements across one at a time:

main.lua
local scores = {90, 85, 78}
local copy = {}
for i = 1, #scores do
copy[i] = scores[i]
end
copy[1] = 0
print("copy[1] is " .. copy[1])
print("scores[1] is " .. scores[1])
Terminal
copy[1] is 0
scores[1] is 90

scores[1] is still 90. This time there really are two tables, and the loop is what made the second one.

Challenge (Optional): Guard the Whole Thing

Write a program that asks for a score number with io.read(), and answers sensibly for any number typed — including 0, including 99, including a word.

Remember what session 4 taught you about what io.read() hands back, and what tonumber does with something that is not a number.

divider

Same Idea, Exam Notation

Last session the exam agreed with Lua. This session it disagrees twice, and these two are the ones most likely to cost you points.

Lua
local scores = {90, 85, 78}
local backup = scores
print(#scores)
print(scores[4])
Exam Notation
scores ← [90, 85, 78]
backup ← scores
DISPLAY(LENGTH(scores))
DISPLAY(scores[4])

1. Assignment makes a copy on the exam

The reference sheet says aList ← bList assigns a copy of the list. So on the exam, backup ← scores gives you two separate lists, and changing one leaves the other alone.

In Lua it does not. You proved that in task 3.

2. An out-of-range index ends the program on the exam

The sheet is explicit: for any list operation, an index below 1 or above the length produces an error message and the program terminates. There is no nil, and nothing after that line runs.

In Lua you get nil and the program keeps going — which is why task 2's error pointed at the wrong line.

LuaExam notation
Number of elements#scoresLENGTH(scores)
backup after assignmentthe same lista copy
scores[4] on a 3-element listnil, keeps runningerror, program ends
How this shows up on the exam. A trace assigns one list to another, changes the second, then displays the first. Answering from Lua habit gives the changed value. The exam's answer is the original. Read the notation before you trace, every time.
divider

Checkpoint

  • Task 1 printing the count and the last element
  • The two line numbers from task 2 written down — the one the error named, and the one the mistake was on
  • A working safe check that answers for a missing score
  • Your task 3 prediction, written before you ran it
  • A real copy in task 4, proved by scores[1] still being 90
divider

Reflection

Answer the following questions before submitting your work.

  1. In task 2 the error message named a different line than the mistake. Explain why, and say what you would look for next time you see an error about nil.
  2. You changed backup and scores changed too. Explain what local backup = scores actually did.
  3. On the exam, the same line makes a real copy. Describe a question you could get wrong by answering from Lua habit, and say which answer the exam wants.
divider

Submit

Submit the required files to the appropriate dropbox.

Activity Complete