Aguarde...

9 de dezembro de 2020

O que é currying em JavaScript e como usá-lo

O que é currying em JavaScript e como usá-lo

Currying é um conceito muito popular na programação funcional , mas pode parecer confuso. Este tutorial ajudará você a entender o que é currying e como funciona. Você também aprenderá como usar currying em JavaScript para ajudá-lo a tornar seu código mais legível e simples.

Uma palavra rápida sobre funções

Antes de começarmos a currying em JavaScript, há algo importante sobre as funções que você deve saber. Em JavaScript, as funções são tratadas como cidadãos de primeira classe . Isso permite que você faça coisas interessantes com eles. Você pode atribuir funções a variáveis ​​e também passá-las como argumentos.

Outra coisa que você também pode fazer é devolvê-los. Você pode retornar funções de outras funções. Não só você pode retornar funções. Você também pode passar argumentos para essas funções retornadas. Tudo isso pode parecer trivial, mas é muito importante. É graças a este currying é possível.

// Example no.2:
// Alternative
function sumNumbers(num1, num2) {
  // Pass the second argument
  // to the returned function
  return function() {
    // Return the sum of all arguments
    return num1 + num2
  }
}

const sum = sumNumbers(11, 91)
sumNumbers()
// Output:
// 102


// Example no.2:
// Or, as a one-liner
const sumNumbers = (num1, num2) => () => num1 + num2

const sum = sumNumbers(52, 25)
sumNumbers()
// Output:
// 77


// Example no.3:
// Create a function that returns a function
function sumNumbers(num1, num2) {
  // Pass the second argument
  // to the returned function
  return function() {
    // Return the sum of all arguments
    return num1 + num2
  }
}

sumNumbers(5, 15)()
// Output:
// 20

Nota: Apenas chamar a sumNumbers()função com alguns números passados ​​como argumentos não fornecerá o resultado que você está procurando. Essa chamada de função retornaria a função que é retornada sumNumbers(). Para obter o resultado, a soma, você também precisa chamar a função retornada.

Uma maneira de fazer isso é chamar a função e atribuir o valor retornado a uma variável. Este valor retornado será a função retornada. Agora, você pode chamar aquela variável, aquela função retornada e obter o resultado, a soma dos números que você passou. Você pode ver isso nos exemplos número 1 e 2.

Uma alternativa é invocar ou chamar ambas as funções. Para fazer isso, você adiciona parênteses ( ()) após a primeira chamada. É quando você chama a sumNumbers()função e a atribui a uma variável. Isso irá chamar a sumNumbers()função, retornar a função de retorno e, em seguida, chamá-la também. Você pode ver essa abordagem no exemplo número 3.

Currying de forma simples

As funções são cidadãos de primeira classe. Uma função pode retornar outra função. Você pode passar argumentos entre essas funções. Agora, vamos falar sobre currying. O que é currying? Currying é um processo de pegar uma função com vários argumentos e transformá-la em uma sequência de funções, cada função recebendo um único argumento.

O resultado é que, em vez de ter, myFunc(arg1, arg2, arg3)você tem myFunc(arg1)(arg2)(arg3). No caso da sumNumbers()função, em vez de sum(num1, num2), a sintaxe seria agora olhar como este: sum(num1)(num2). Se você usar mais argumentos, adicionará mais parênteses. Você notou algo interessante nesta sintaxe?

// Simple example of calling syntax
// Create curried function
function myCurriedFunc(arg1) { /* ... */ }

// Call curried function
// One pair of parentheses for each returned function
myCurriedFunc(arg1)(arg2)(arg3)(arg4)(arg5)

Você também está adicionando o segundo par de parênteses após a chamada da função, ou mais pares. Isso se parece muito com o que você viu no exemplo número 3, onde você invocou a função retornada imediatamente. Portanto, é assim que curry em JavaScript se parece quando você invoca uma função curried. Agora, vamos dar uma olhada sob o capô.

Sob o capô, parte 1

Vamos mantê-lo simples, apenas o suficiente. Imagine que você tem uma função. Esta função retorna outra função. Quando você deseja chamar ambas as funções, você adiciona um par de parênteses após o primeiro ao chamar a função externa. Este segundo par de parênteses é para a segunda função, a função que você retorna.

Uma maneira simples de pensar sobre isso é que o segundo par é outra chamada de função. Nesse caso, ele está chamando a função retornada. Aqui está o interessante. Se a função retornada também retorna uma função, basta repetir o processo. Você adiciona o terceiro par de parênteses.

E se você retornar o evento de função mais vezes? Tudo o que você precisa fazer é repetir o mesmo processo várias vezes. Você adiciona parênteses tantas vezes quanto as funções retornadas. Um parênteses é para cada função retornada. Esta é a primeira parte, como funcionam os parênteses adicionais.

// Example of calling a function
// that returns one function
function myFunc() {
  return function() {
    return 'Hello'
  }
}

// Calling the function
// One pair of parentheses
// for the outer function
// and one for each returned function
myFunc()()
// Output:
// 'Hello'


// Example of calling a function
// that returns two functions
function myFunc() {
  return function() {
    return function() {
      return 'Hello'
    }
  }
}

// Calling the function
// One pair of parentheses
// for the outer function
// and one for each returned function
myFunc()()()
// Output:
// 'Hello'


// Example of calling a function
// that returns four functions
function myFunc() {
  return function() {
    return function() {
      return function() {
        return 'Hello'
      }
    }
  }
}

// Calling the function
// One pair of parentheses
// for the outer function
// and one for each returned function
myFunc()()()()
// Output:
// 'Hello'

Dependendo do seu conhecimento de programação e JavaScript, ainda pode haver alguma confusão sobre como esses parênteses adicionais funcionam. O que pode ajudá-lo é imaginar esses parênteses de uma forma diferente. Em vez de vê-los todos em uma única linha, imagine-os em linhas separadas, um parênteses por linha.

Em seguida, imagine que há uma nova variável criada para cada chamada de função. A primeira chamada de função é atribuída a uma nova variável. Então, a mesma variável é chamada. Esta chamada de função retorna um novo valor. Este valor é a função retornada. Esta função é atribuída a uma nova variável.

Esse processo de chamada e atribuição se repete tantas vezes quanto as funções retornadas. Quando a última função é chamada, o valor final é retornado. Isso é, menos ou mais, a mesma coisa que acontece quando você usa os parênteses alinhados em uma linha. Espero que esta explicação ajude.

// Curried function
function myFunc(arg1) {
  return function(arg2) {// First returned function
    return function(arg3) {// Second returned function
      return function(arg4) {// Third returned function
        return `${arg1}, ${arg2}, ${arg3}, ${arg4}`
      }
    }
  }
}

myFunc('arg1')('arg2')('arg3')('arg4')
// Output:
// 'arg1, arg2, arg3, arg4'

// Is similar to:
const firstReturnedFunc = myFunc('arg1')
const secondReturnedFunc = firstReturnedFunc('arg2')
const thirdReturnedFunc = secondReturnedFunc('arg3')
const finalValue = thirdReturnedFunc('arg4')

console.log(finalValue)
// Output:
// 'arg1, arg2, arg3, arg4'

Sob o capô, parte 2

A segunda parte é sobre como transformar esses argumentos. Até agora, você passou todos os argumentos para uma única chamada de função, a primeira. Isso pode ser bom por enquanto, mas pode rapidamente se tornar confuso. Distribuir esses argumentos para as chamadas de função pode ajudá-lo a tornar seu código mais limpo e mais fácil de ler, teoricamente.

Uma coisa boa é que essa transformação é muito fácil. Existem apenas duas alterações que você precisa fazer. Primeiro, você deve parar de definir todos os parâmetros na primeira função externa. Em segundo lugar, o que você precisa fazer é definir esses parâmetros para cada função retornada. Cada uma dessas funções terá um desses parâmetros.

Então, a última função retornada fará algo com todos esses argumentos e retornará algo. Esta pequena mudança permitirá que você passe todos os argumentos necessários individualmente para cada chamada de função, um argumento para cada par de parênteses. É isso aí. Este é todo o mistério sobre currying em JavaScript e em geral.

// Example with two arguments
function myFunc(arg1) {
  return function(arg2) {
    return arg1 + arg2
  }
}

// Calling the function
myFunc(15)(59)
// Output:
// 74


// One-line alternative
const myFunc = (arg1) => (arg2) => arg1 + arg2


// Example with three arguments
function myFunc(arg1) {
  return function(arg2) {
    return function(arg3) {
      return arg1 * arg2 * arg3
    }
  }
}

// Calling the function
myFunc(3)(5)(7)
// Output:
// 105


// One-line alternative
const myFunc = (arg1) => (arg2) => (arg3) => arg1 * arg2 * arg3


// Example with four arguments
function myFunc(arg1) {
  return function(arg2) {
    return function(arg3) {
      return function(arg4) {
        return arg1 + arg2 + arg3 + arg4
      }
    }
  }
}

// Calling the function
myFunc(56)(23)(13)(89)
// Output:
// 181


// One-line alternative
const myFunc = (arg1) => (arg2) => (arg3) => (arg4) => arg1 + arg2 + arg3 + arg4

Você se lembra da sumNumbers()função do início deste artigo? Vamos reescrever para uma função curried. Uma vez que esta função é muito fechada para uma versão curry, a reescrita será rápida. A única coisa que você precisa fazer é distribuir os parâmetros entre as chamadas.

Você deve pegar o num2parâmetro da função externa e usá-lo como um parâmetro para a função retornada. É isso aí. Agora, você tem uma versão curry da sumNumbers()função.

// Before
function sumNumbers(num1, num2) {
  return function() {
    return num1 + num2
  }
}

sumNumbers(52, 77)()
// Output:
// 129


// After
function sumNumbers(num1) {
  return function(num2) {
    return num1 + num2
  }
}

sumNumbers(52)(77)
// Output:
// 102

Vários argumentos por chamada

Até agora, você trabalhou com exemplos que sempre usaram apenas um argumento por chamada. Isso não significa que você deve sempre usar apenas um argumento. Se quiser usar mais, você pode. Nesse caso, tudo o que você precisa fazer é decidir qual de suas funções aceitará esses argumentos adicionais.

Ao decidir, você deve adicionar novos parâmetros necessários a essa função e você está pronto para começar. Depois disso, ao chamar sua função curried, você poderá passar vários argumentos para chamadas de funções específicas, par de parênteses. Se você fizer isso, lembre-se de usar a quantidade correta de parâmetros para cada chamada.

// Example of multiple arguments per call
// This function will accept one parameter
function myFunc(arg1) {
  // This function will also accept one parameter
  return function(arg2) {
    // This function will accept three parameters
    return function(arg3, arg4, arg5) {
      // This function will accept one parameter
      return function(arg6) {
        return arg1 * arg2 * arg3 * arg4 * arg5 * arg6
      }
    }
  }
}

// Call myFunc
myFunc(1)(3)(5, 7, 9)(11)
// Output:
// 10395

A ordem dos argumentos é importante

Há uma coisa que você precisa saber sobre currying. A ordem dos argumentos é importante. Isso pode soar como um acéfalo, mas ainda é bom mencioná-lo. Se você alterar a ordem dos argumentos que passa para as chamadas de função individuais, também altera o valor que cada função receberá.

Dependendo da função do curry, isso pode alterar o resultado que você obterá. Se você passar argumentos de tipos de dados errados, também poderá quebrar seu código. Portanto, verifique novamente se você passou o (s) argumento (s) correto (s) para a chamada correta. Além disso, verifique se você está usando o número correto de argumentos. Isso também pode causar muitos problemas.

Postado em Blog
Escreva um comentário