Is there anything in ADVPL equivalent to Java lambda function?

Asked

Viewed 116 times

3

I am maintaining an ADVPL project. In it, I have some source files. Among these sources, I have an information miner in the GEO1 file and a communicator of the information mined in the GEO3 file.

In the initial operation, the loads were low and first the entire extraction was done to communicate to the external system about these changes. It turns out that this initial load is higher than initially expected, so I’m refactoring the system to, during the extraction, run the upload.

If it was something in Java, which I have more usual, I would take a more functional approach, passing a Consumer to my extractor. It would be as if the following were the original code:

class Minerador {
  public List<InformacaoMinerada> mineraInformacoes() {
    List<InformacaoMinerada> info = new ArrayList<>();

    while (temInformacaoMineravel()) {
      info.add(minerarProximaInformacao());
    }

    return info;
  }
  private InformacaoMinerada minerarProximaInformacao() {
    // código de mineração que posso desconhecer, lei de Deméter
    ...
    return stuff;
  }
}

class Comunicador {
  public void enviaDados(List<InformacaoMinerada> informacoes) {
    // detalhes internos do envio
  }
}

class Principal {
  public static void main() {
    Minerador miner = getMiner();
    Comunicador mercurio = getComunicador();

    mercurio.enviaDados(miner.mineraInformacoes());
  }

  public static Minerador getMiner() {
    // inicia o minerador corretamente
    return miner;
  }

  public static Comunicador getComunicador() {
    // inicia o minerador corretamente
    return comunicador;
  }
}

In the transformation, I would have the communicator notified that there is new information and, according to a Threshold, send the received information:

class Minerador {
  public void mineraInformacoes(Consumer<InformacaoMinerada> consumidorInformacao) {
    List<InformacaoMinerada> info = new ArrayList<>();

    while (temInformacaoMineravel()) {
      consumidorInformacao.accept(minerarProximaInformacao());
    }
  }

  private InformacaoMinerada minerarProximaInformacao() {
    // código de mineração que posso desconhecer, lei de Deméter
    ...
    return stuff;
  }
}

class Comunicador {
  private List<InformacaoMinerada> informacoesBufferizadas;
  private int threshold;

  public void enviaDadosBufferizados() {
    if (informacoesBufferizadas.size() > 0) {
      enviaDados(informacoesBufferizadas);
      informacoesBufferizadas.clear();
    }
  }

  public void adicionaInformacao(InformacaoMinerada info) {
    informacoesBufferizadas.add(info);
    if (informacoesBufferizadas.size() >= threshold) {
      enviaDadosBufferizados();
    }
  }

  private void enviaDados(List<InformacaoMinerada> informacoes) {
    // detalhes internos do envio, mesmo código da enviaDados antiga
  }
}

class Principal {
  public static void main() {
    Minerador miner = getMiner();
    Comunicador mercurio = getComunicador();

    miner.mineraInformacoes(mercurio::adicionaInformacao);

    // para eventual envio de dados residuais
    mercurio.enviaDadosBufferizados();
  }

  public static Minerador getMiner() {
    // inicia o minerador corretamente
    return miner;
  }

  public static Comunicador getComunicador() {
    // inicia o minerador corretamente
    return comunicador;
  }
}

There is something equivalent to this lambda function in ADVPL?

2 answers

2


There is something called Code Block, which is even an enclosure too (with the same problems that most languages have when they run in scenarios like a loop for example). The idea is just this, but the internal functioning is bad.

It has existed since the Clipper and there was well implemented. In Harbour they went a little further and created a reference for the simplest cases that do not need closures, giving more efficiency. Deep down it was a pointer, so they even call the guy Pointer, although technically it is a reference (just as they call the Hash a guy who has two distinct implementations in the same type, is terrified, and neither of the two is a hashand have seen that they are not good at naming things).

Clipper always had something called macro, because dBase had, and dBase was mandatory because it didn’t have the right mechanisms. Clipper created these mechanisms better, but kept the macro for compatibility. It is basically a compiler, it can generate execution from a text. Something like a eval(). Used with an "operator" &. Imagine that it is very slow and susceptible to errors, not to mention insecurity. That’s why the code block was created. But ADVPL created only the macro, and to maintain compatibility with the syntax of Code Block they transformed it into macro internally, IE, the performance is suffering.

It works that way:

aArray := { 0, 1, 2, 3 }
nValor := 5
Scan(aArray, {|i| conout(i + nValor) })

user function Scan(aArray, bAction) {
    for i := 0 to len(aArray)
        eval(bAction)
    next
return nil

I put in the Github for future reference.

Will print from 5 to 8.

In this case I used cloister in nValor, but it is not necessary if there is no need. Use sparingly, it was just to give a full example.

1

It exists, it’s called a code block (block code).

You can see a code block usage in the following example:

// arquivo Test1.prw

static function apendar(aArray, cStr)
  // se o threshold for 2:
  if len(aArray) >= 2
    msgInfo('fazendo a chamado do threshold e limpando o vetor')
    aArray := {}
  endif

  aAdd(aArray, cStr)
return

user function principal()
  local aVetor := {}
  local cVetorLegivel := ''
  local i

  u_fext('1', {|cStr| apendar(aVetor, cStr)})
  u_fext('2', {|cStr| apendar(aVetor, cStr)})
  u_fext('3', {|cStr| apendar(aVetor, cStr)})
  u_fext('4', {|cStr| apendar(aVetor, cStr)})

 for i := 1 to len(aVetor)
   cVetorLegivel  += aVetor[i] + ','
 next i
 MsgInfo(cVetorLegivel)
return
// arquivo Test2.prw

user function fext(cStr, bBloco)
  eval(bBloco, cStr)
  MsgInfo(cStr)
return

Note that the function fext completely unaware of the implementation of bBloco, but is able to call it correctly through the eval.

I ran tests passing the first argument of apendar both with the variable reference aVetor as using it directly and the results were equal:

// bloco de código usando `aVetor` diretamente
{|cStr| apendar(aVetor, cStr)}

// bloco de código usando a referência a `aVetor`
{|cStr| apendar(@aVetor, cStr)}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.