2.2.1 - Scope, subroutines and parameter passing

2.2.1 - Scope, subroutines and parameter passing

A subroutine needs a clear way to receive data, produce results and control which state it can change. You will use local and global variables, distinguish functions from procedures, and trace value and reference parameters. Python examples will show why sharing an object is different from giving a procedure direct access to a caller's variable.

Scope controls access to a variable

The scope of a variable is the region of a program where its name can be accessed. A local variable belongs to a subroutine's scope; a global variable is accessible across the program's subroutines, subject to the language's naming rules. In OCR pseudocode, variables created inside a function or procedure are local, and global can make a main-program variable global.

Pseudocode
global score = 10
function bonus(points)
    score = points + 2
    return score
endfunction
result = bonus(3)
print(str(result))
print(str(score))

Here score = points + 2 declares a local score within bonus; it does not change the global score. The results printed are 5 then 10. The two names refer to different variables. Shadowing means a local name hides an outer name within its scope. Clearer names, such as adjusted_points, reduce the chance of mistaking one for the other.

Python has the same distinction in this example: assignment inside a function creates a local binding unless declared otherwise. To rebind a module-level name from a Python function, explicitly declare it global score inside that function. Merely reading a global value can work without this declaration. A function that assigns to score treats score as local throughout that function, so trying to read it before its local assignment can fail. Avoid relying on a vague rule that every same-named assignment changes the global.

Changing one declaration in this ungraded example lets you compare the two effects without changing its caller. Predict each version before running it.

Choose which binding changes

{"exercise_id":"h446-study-local-global-binding","task_version":"1","runtime_profile":"python-console-v1","original_filename":"scope_binding.py","file_mode":"fresh","code":"level = 10\n\ndef reset_level():\n level = 2\n return level\n\nprint("returned", reset_level())\nprint("main", level)\n","instructions":"Ungraded exploration. Predict both outputs, then run. Add global level as the first indented statement inside reset_level, predict again and run. Explain why the second print now changes. Remove the declaration and rerun to distinguish local rebinding from deliberate global rebinding."}

The declaration changes which binding the assignment targets. It does not turn Python into OCR byRef passing: no reference parameter is present in this example.

Scope and lifetime answer different questions: where a name is accessible, and how long its state exists. A global normally persists for the program run; each call has its own local parameters and temporary state. A returned object can outlive a Python call, so returning a list does not destroy the list when its local name disappears. For ordinary scalar trace exercises, record each call's local values separately.

Functions and procedures make modules usable

Modularity divides a program into subroutines with defined responsibilities and interfaces. An interface states the data accepted, the result produced and any side effects. A caller can use a module without repeating its internal instructions.

A function returns a result that can be used in an expression. A procedure performs an action without returning such a result. In OCR pseudocode, the two are explicitly labelled:

Pseudocode
function delivery_cost(distance)
    if distance <= 5 then
        return 2
    else
        return 4
    endif
endfunction

procedure show_cost(amount)
    print("Delivery: " + str(amount))
endprocedure

charge = delivery_cost(8)
show_cost(charge)

Assume distance is non-negative. delivery_cost(8) returns 4; show_cost(4) displays Delivery: 4. The parameter distance is the name used in the definition; the argument 8 is the value supplied by this call. Each call can supply a different argument. A return ends that function call and hands the value back; printing writes output and does not substitute for the returned result.

Python uses def for both styles. A function without an explicit value return returns None, so a procedure-style def show_cost(amount): is still a Python function technically. The useful design distinction is whether callers depend on a result or an action. Assigning charge = show_cost(4) would give None, not 4.

Passing distance and returning charge makes delivery_cost easy to test independently: 0,5,6 should return 2,2,4. It does not need user input or a screen to calculate. Keeping display separate also lets a different caller reuse the calculation. A bug fix to one well-defined module can then serve every caller, though interface changes must be checked at each call site.

Value and reference parameters permit different changes

Passing by value supplies a separate parameter value: reassigning that parameter does not reassign the caller's variable. Passing by reference makes the parameter refer to the caller's variable, so an assignment through the parameter can change that variable. OCR assumes value passing unless a task states otherwise, and uses byVal and byRef where the distinction is relevant.

OCR byRef permits an assignment through a parameter to change the caller's variable. In Python, parameter rebinding is local; mutation can instead change a shared object. Trace the operation and the object affected.

Pseudocode
procedure adjust(x:byVal, y:byRef)
    x = x + 3
    y = y + x
endprocedure

a = 4
b = 10
adjust(a, b)
print(str(a))
print(str(b))

Continue this lesson.

You’re previewing NovaLearn without an account. Create a free account to read the rest.

Create free account