Percursos
/
jq
jq
/
Exercícios
/
Estatísticas de Notas
Estatísticas de Notas

Estatísticas de Notas

Exercício de aprendizagem

Introdução

Reduce

Suponhamos que queremos somar um array de números. Há muitas formas de atingir este objetivo.

  • Em muitas linguagens, este problema é expresso como um ciclo com uma variável acumuladora.

  • Este problema pode ser escrito como uma função recursiva. Em pseudocódigo, poderíamos ter isto.

    function Add(X, Sum=0):
      if X is empty then
        return Sum
      else
        return Add(rest(X), Sum + first(X))
      end
    end
    

    Este método de dividir o problema em partes mais pequenas também pode ser descrito como "reduzir em direção ao caso base".

Reduce é uma forma de combinar todos os elementos de uma estrutura de dados num único valor. O processo itera sobre a estrutura de dados, aplicando uma função a cada elemento para atualizar o resultado acumulado.

Em jq, este processo é implementado no filtro reduce. Noutras linguagens, pode chamar-se "fold", "fold-left", "inject" ou "aggregate".

A expressão reduce do jq tem este aspeto.

reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
  • STREAM_EXPRESSION é um fluxo de itens, cada um guardado na variável $var por sua vez.
  • INITIAL_VALUE é o valor inicial do resultado acumulado (conhecido como o "acumulador").
  • A UPDATE_EXPRESSION combina ("fold") o valor atual ($var) no acumulador.
    • No contexto desta expressão, . é o valor do acumulador.
    • O resultado da expressão é guardado no acumulador para ser usado na iteração seguinte.
    • Após a última iteração, o resultado acumulado é o resultado de reduce.

Vamos ver um exemplo: somar os números de um array. O filtro add faz precisamente isto, mas vamos ver como implementá-lo.

Se usarmos [10, 20, 30, 40] como entrada e tomarmos zero como estado inicial, cada passo tem este aspeto.

# estado elemento redutor resultado
1 0 10 0 + 10 10
2 10 20 10 + 20 30
3 30 30 30 + 30 60
4 60 40 60 + 40 100

Em sintaxe jq, isto tem o aspeto deste código.

0 + 10 | . + 20 | . + 30 | . + 40

Podemos exprimir isso com o filtro reduce.

[10, 20, 30, 40] | reduce .[] as $n (0; . + $n)     # => 100
Note

O add integrado é, na verdade, implementado com reduce, mas usa "null" como estado inicial (qualquer tipo de dados pode ser adicionado a null).

def add: reduce .[] as $x (null; . + $x);

Algumas coisas a ter em conta

  • Na expressão de redução, . é o acumulador. Se a entrada for um objeto que precisas de referenciar dentro da função de redução, tens de o guardar numa variável.

    {"apple": 10, "banana": 16, "carrot": 4}
    | . as $obj
    | reduce (keys | .[]) as $key (0; . + $obj[$key])     # => 30
    
  • O acumulador pode ser de qualquer tipo de dados. Por exemplo, podes querer inverter um array.

    ["A", "B", "C", "D"]
    | reduce .[] as $elem ([]; [$elem] + .)       # => ["D", "C", "B", "A"]
    

Instruções

És professor(a). No final do ano, atribuíste uma nota numérica a cada um dos teus alunos. Agora precisas de converter essas notas em notas alfabéticas e de contar quantos alunos obtiveram cada uma.

1. Converter uma nota numérica numa nota alfabética

A função letter_grade recebe uma nota numérica como parâmetro de entrada e devolve a letra. Usa estes intervalos:

Letra Nota
A 90% - 100%
B 80% - 89%
C 70% - 79%
D 60% - 69%
F 0% - 59%

Exemplo:

75 | letter_grade   # => "C"

2. Contar o número de alunos em cada nota alfabética

A função count_letter_grades recebe um objeto que associa os nomes dos alunos às respetivas notas. O resultado é um objeto que associa cada nota alfabética ao número de alunos com essa nota.

Exemplo:

{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}

Há várias formas diferentes de resolver isto. Usa o filtro reduce para praticar.

Editar via GitHub A ligação abre numa nova janela ou separador
jq Exercism

Estás pronto para começar Estatísticas de Notas?

Inscreve-te no Exercism para aprenderes e dominares jq com 12 conceitos75 exercícios, e mentoria humana real, tudo grátis.