Programming: Doing More with Less (The Principle of Efficiency and DRY)

Programming: Doing More with Less (The Principle of Efficiency and DRY)

What Are We Trying to Do When We Write Code?

When a program is written, the goal is for the computer to perform specific tasks. At a more detailed level, programs often involve processing data, executing actions based on that data, and then either displaying results or using them for further processing.

What Affects How We Write Code?

Several factors influence how code is written:

Programming Language:

The programming language provides a set of "units of operation" that can be performed by the computer processor. Different languages can achieve the same result, but the code will look different based on each language's syntax and fundamental "units of operation."

What is a unit of operation?

A unit of operation is an instruction or action that can be performed in one line of code in a programming language. Everything that can be done in a single line of a language represents its units of operation.

Here is a list of languages and their units of operation:

  • Assembly (x86 Language):

- MOV R1, 42 (move value 42 into register R1)
- ADD R2, R1, R3 (add values in R1 and R3, store in R2)
- JMP label (jump to a labeled instruction)        

  • C, C++:

- x = 5; (Store value in labeled memory)
- Sum = a + b; (add two values and store result in labeled memory)
- Add(); (calling function or calling a stored procedure/predefined instructions)        

  • SQL (Declarative Language):

- Select name From table_name Where condition; (get a collection of records based on a condition)
- Create Table table_name (column1 datatype, column2 datatype); (create a table for storing records)
- Select Count(*) From table_name; (perform aggregation operations like sum, count, average, etc.)        

  • Scheme (Functional Language):

- (Number->string 1001 2) (converting number to string)(
- define number 10) (create a labeled variable and store 10 in it)
- (define (square x) (* x x)) (define a function that outputs the square of the input)        

  • Scratch (Visual Programming Languages):Drag and Drop nodesConnect nodes

Each language has its own units of operation. Depending on the units of operation a language offers, it may be more suitable for certain tasks than other programming languages.

When code is written, the sequence of "units of operation" the computer should execute is being dictated. From these instructions, applications like Photoshop or games like GTA are built.

Input Data Structure and Desired Output:

Beyond the choice of language, the specific sequence of instructions needed is heavily influenced by the data available and the specific result that is desired.

Consider a mathematical example involving quadratic equations to illustrate how both the structure of input data and the desired output affect how code is written.

  • Scenario: A program needs to process a quadratic equation.
  • Input Data Form A: The quadratic equation is provided using the standard form ax^2 + bx + c = 0. The input parameters might be a, b, and c.
  • Input Data Form B: The quadratic equation is provided using the vertex form a(x+h)^2 + k. The input parameters might be a, h, and k.
  • Desired Output X: The roots of the equation.
  • Desired Output Y: The coordinates of the turning point (vertex).

The code required to achieve the desired output depends critically on the form of the input data and what specific output is needed.

  • To get Output X (roots) from Input A (ax^2 + bx + c = 0), the code would typically implement the quadratic formula: x = (-b ± sqrt(b^2 - 4ac)) / 2a. This requires parameters a, b, and c.
  • To get Output X (roots) from Input B (a(x+h)^2 + k), the code would first need to convert the vertex form into the standard form or solve the equation algebraically, which is a different set of operations than using the quadratic formula directly.
  • To get Output Y (turning point) from Input A (ax^2 + bx + c = 0), the code would calculate the x-coordinate using -b / 2a and then substitute that value back into the equation to find the y-coordinate.
  • To get Output Y (turning point) from Input B (a(x+h)^2 + k), the code directly uses the parameters h and k, as the vertex is at (-h, k). This is a much simpler set of operations for this specific output.

This example shows that regardless of the programming language, the specific instructions and logic implemented are directly shaped by the structure of the input data (which form of the equation is provided) and the exact nature of the desired output (roots or vertex).

Why Create Multiple Different Coding Languages?

Processors fundamentally understand machine code. The difference between assembly and machine code is minimal, so it can be said that the processor understands assembly language.

All other languages must be converted (compiled or interpreted) into assembly language to run on the processor. Given this, why were these higher-level languages created?

Online resources list many reasons for the creation of higher-level languages. Some of the common reasons include:

  • Ease of Use and Readability
  • Faster Development and Maintenance
  • Portability Across Platforms
  • Reduction in Complexity
  • Error Prevention and Debugging (due to debugging tools and type checking features not typically found in assembly code)
  • Improved Collaboration
  • Support for Large-Scale Applications
  • Reusability and Abstraction
  • Cost Efficiency (facilitated by faster development cycles and easier maintenance)

These are some of the primary reasons for the development and widespread use of different coding languages, especially higher-level ones.

However, underlying many of these reasons is a core principle: DRY (Don't Repeat Yourself).

Principle Of Efficiency

The DRY principle is typically introduced as a concept of reducing code repetition. It can be argued that DRY is a specific application of efficiency, a principle practiced across many fields, not just coding. Efficiency, in this context, is about achieving a goal using less "work" or "human input." Efficiency is a key goal in software development.

Let's revisit the reasons for higher-level languages from the perspective of efficiency:

  • Ease of Use and Readability: Less mental effort and time are required to read, understand, and use the language, making the programmer more efficient.
  • Faster Development and Maintenance: Higher-level languages reduce the time and effort needed to build and maintain applications, making the development process more efficient.
  • Portability Across Platforms: Instead of writing different code for each processor type to perform the same task, one codebase can run on multiple platforms. This highlights code and work efficiency, as repeating the effort for each platform is avoided.
  • Reduction In Complexity: Complex tasks can be accomplished with simpler instructions thanks to the abstractions provided by high-level languages. For example, reading from a file might take just two lines in Python, but could require 40 or more lines in assembly. This is efficiency achieved by abstracting repetitive low-level steps.
  • Error Prevention And Debugging: Debugging in assembly without assistive tools is challenging and time-consuming. Even in higher-level languages, debugging is difficult without proper tools. The features that aid in quickly identifying and fixing errors directly contribute to developer efficiency.
  • Improved Collaboration: Tools like Git and the internet have made collaboration far easier. Merging codebases, once an inefficient manual process, can now be done efficiently with commands like git merge. This is efficiency in managing collaborative code development.
  • Support for Large-Scale Applications: Building and maintaining large, complex software systems demands significant efficiency. DRY principles and the abstraction provided by higher-level languages are crucial for managing complexity and enabling collaboration on a scale that would be infeasible at the assembly level.
  • Reusability and Abstraction: Reusability means leveraging existing code or tools instead of starting from scratch, which dramatically speeds up software development. Abstraction allows complex underlying operations to be handled automatically— the developer specifies what they want, not how the hardware performs it. For example, printing text to a console or managing memory are tasks handled by the language/operating system. These abstractions encapsulate complex, repetitive low-level operations, enabling faster, more efficient development and promoting DRY-ness.
  • Cost Efficiency: All the previously mentioned points—faster development, easier maintenance, reduced errors, improved collaboration, reusability, and abstraction—contribute to significant cost reductions in software development over time by requiring less human effort per unit of functionality.

Efficiency is a principle that spans multiple fields, including mechanical engineering, mechatronics, the medical field, and more. Consider the following examples, which demonstrate achieving a goal with less human effort or input:

  • Using a car versus walking—a car requires less effort for travel, especially over long distances.
  • Using a phone versus sending a messenger pigeon.
  • The convenience of fast food versus preparing a meal from scratch.
  • The machinery currently used by farmers versus farming by hand.
  • A sewing machine versus using just a needle and thread.

All these examples illustrate achieving a goal with less input or effort. That's the essence of efficiency—it's a ubiquitous concept that goes by many names or phrases, such as:

  • Less is more
  • Don't repeat yourself
  • Keep it simple, stupid (KISS)
  • Don't reinvent the wheel
  • And many others.

In software, the goal is defined, and generally, the less code written to achieve that goal efficiently and correctly, the better. Many features and programming principles exist specifically to increase efficiency by reducing the amount of code that needs to be explicitly written or repeated. Some of these features include:

  • Functions: Allow defining reusable blocks of code that can be executed multiple times with different inputs, avoiding the repetition of code blocks.
  • Classes & OOP Inheritance: Classes bundle data and methods, encapsulating related logic. Inheritance allows new classes to reuse functionality from existing ones, avoiding repetitive code.
  • Polymorphism: Enables code to work with objects of different types through a single interface, reducing the need for repetitive conditional checks based on type.
  • Templates (C++) / Generics (C# and Java): Allow writing code that works with any data type, avoiding the need to rewrite the same logic for different types (e.g., a generic list that can hold any type of object).
  • Higher-Order Functions: Functions that operate on other functions, abstracting common code patterns like iteration and transformation and reducing redundant manual loops.
  • Macros: Define reusable code snippets that are expanded at compile time, reducing repetition in specific contexts.
  • Code Generators: Tools or scripts that write other code automatically, saving significant time and effort by automating the writing of repetitive code patterns.

Additionally, many languages include unique syntactic features or libraries designed to minimize the amount of code needed for common tasks (sometimes called "syntactic sugar" or shortcuts). List comprehensions in Python are a classic example of a shortcut for writing common loop patterns efficiently. These features all contribute to writing less code, making the programmer more efficient, and adhering to the DRY principle.

CONCLUSION

Achieving project goals often benefits from writing less code and minimizing effort. By leveraging the principles and language features discussed here, developers can find ways to improve efficiency and adhere to the DRY principle in their work.

To view or add a comment, sign in

More articles by Mubarak Nurudeen Salley

Others also viewed

Explore content categories