Bloody parentheses

Forbandede parateser
Author

Søren O’Neill & Steen Harsted

Published

February 8, 2024


1 The problems with parentheses

Functions take their input parameters enclosed in parentheses like e.g. mean(1:5).

Often, you will want to use the output of one function as the input for another function.

This leads to two common issues. Look at this simple example which picks a random sample of 10 numbers from the range of 1-to-100, calculates the square root of each number, and calculates the arithmetic mean:

mean(sqrt(sample(1:100, 10)))

# ... or slightly more human-readable form:
mean(
  sqrt(
    sample(1:100, 10)
    )
  )

Notice, two thing:

  1. you have 3 nested sets of parentheses - it is very easy loose track of how they pair up (or not).
  2. you code is actually executed top-down and left-to-right as mean() first and sample() last. The mean() function is executed first, but then pauses to wait until the sqrt() function returns a result. The sqrt() function in turn will also pause and wait for the result of the sample() function. In a sense therefor, the code is written left-to-right, but in reality completed in the order right-to-left. When designing the code, you sort of have to write your code backwards.

2 Help is at hand

2.1 Rainbow parentheses

Look into the menu Tools / Global Options / Code / Display / Use rainbow parentheses.

This option will color nested parentheses pairwise in different colors, making it easier to differentiate the matching parentheses.

Also notice, that when your cursor is placed immediately before a starting parentheses or immediate following a closing parenthesis, RStudio will highlight the corresponding parenthesis – note that this may not work as expected if there are missing or extra parantheses.

2.2 Use pipes

Many, but not all functions are pipe-friendly.

Using pipes, the example above can be written as:

sample(1:100,10) |> sqrt() |> mean()

In essence, the |> pipe takes the output from the left-hand side and inserts it as (the first) input in the right hand side. Look at the slightly different example below:

sample(1:100,10) |> sqrt() |> mean(na.rm=TRUE)

We explicitly set the parameter na.rm to TRUE in the function mean(). R will know to use the output of sqtr() as the first input to mean() (and only then, add the parameter na.rm) … looking in the help file for mean(), you can learn that the first parameter expected x is data for which a mean can be calculated.

3 Problemer med parenteser

Funktioner tager deres inputparametre indkapslet i parenteser, som f.eks. mean(1:5).

Ofte vil du gerne bruge outputtet fra en funktion som input til en anden funktion.

Dette fører til to almindelige problemer. Se på dette enkle eksempel, der vælger en tilfældig stikprøve på 10 tal fra intervallet 1 til 100, beregner kvadratroden af hvert tal og dernæst beregner det aritmetiske gennemsnit:

mean(sqrt(sample(1:100, 10)))

# ... eller i en lidt mere læsbar form:
mean(
  sqrt(
    sample(1:100, 10)
    )
  )

Bemærk to ting:

  1. Du har 3 indlejrede sæt parenteser – det er meget nemt at miste overblikket over, hvordan de parvis passer sammen (eller ikke gør).
  2. Din kode udføres faktisk fra top til bund og venstre mod højre, men rækkefølgen på udførelsen er mean() først og sample() sidst. Funktionen mean() kaldes først, men venter på, at sqrt() returnerer et resultat. sqrt() venter så igen på resultatet fra sample(). På en måde skrives koden altså fra venstre mod højre, men den udføres i praksis fra højre mod venstre. Når du designer koden, skal du derfor nærmest skrive den baglæns.

4 Hjælp er inden for rækkevidde

4.1 Regnbueparenteser

Gå ind i menuen Værktøjer / Globale Indstillinger / Kode / Visning / Brug regnbueparenteser.

Denne mulighed vil farve indlejrede parenteser parvis i forskellige farver, hvilket gør det lettere at skelne mellem de matchende parenteser.

Bemærk også, at når din markør er placeret lige før en åbningsparentes eller lige efter en lukkende parentes, vil RStudio fremhæve (hvad den mener er) den tilsvarende parentes.

4.2 Brug pipes

Mange, men ikke alle funktioner er pipe-venlige.

Ved brug af pipes kan eksemplet ovenfor skrives som:

sample(1:100,10) |> sqrt() |> mean()

Essensen af |>-pipen er, at den tager outputtet fra venstre side og indsætter det som (det første) input i højre side. Se på det lidt anderledes eksempel nedenfor:

sample(1:100,10) |> sqrt() |> mean(na.rm=TRUE)

Her sætter vi eksplicit parameteren na.rm til TRUE i funktionen mean(). R ved, at outputtet fra sqrt() skal bruges som det første input til mean() (og derefter tilføjes parameteren na.rm). Hvis du ser i hjælpesiden for mean(), vil du opdage, at den første forventede parameter x er data, som der kan beregnes et gennemsnit for.