GUJ Discussões   :   últimos tópicos   |   categorias   |   GUJ Respostas

Laço de repetição

Imaginem o seguinte senário.

Você tem uma condição if;
E dentro deste, há um laço de repetição for;
E dentro deste, há uma impressão padrão System.out.print;
O que deve ser feito para que esta impressão seja realizada apenas uma vez, porém, sem que esta seja retirada de dentro do laço for?

Obs: o for deve ignorar a impressão, mas deve fluir normalmente com outros elementos contidos nele.

você pode usar um break

if (condicao) {
    for(int i = 0 ; i < limite ; i++) {
        // lógica
        System.out.println("Imprime alguma coisa");
        break;
    }
}

Serviria. No entanto, o for precisaria trabalhar com outros elementos contidos nele, além da impressão. E o break impediria que isso ocorresse.

Tens outra possibilidade em mente?

Então você precisa ser mais claro nos seus requisitos. Dizer que ele “precisaria trabalhar com outros elementos contidos nele” não dá muita clareza no seu problema.

Perdão, meu caro. Imaginei que fosse lógico. Pois não haveria sentido o usuário usar o for para realizar apenas uma impressão. Logicamente, haveria de ter pelo menos um outro elemento que fosse útil ao laço.

Faz sentido se o seu objetivo for varrer uma lista e imprimir apenas o elemento que te interessa

1 Curtida

@joaostm esta é só uma situação hipotetica que vc imaginou ou vc encontrou um problema real que requer esta solução?

É que dependendo do que for, é quase certeza de que isto poderia ser resolvido mais facilmente fora do loop.

Se vc puder compartilhar mais sobre seu problema seria legal. De qualquer forma, eis algumas possibilidades:

class Main {
    public static void main(String[] args) {
       String[] frutas = {"banana", "maçã", "laranja", "limão", "abacaxi"};

       for(int i = 0, length = frutas.length; i < length; i++) {
           if (i == 0)
               System.out.printf("Imprime somente na primeira iteração do loop: %d\n", i);

           if ("laranja".equals(frutas[i]))
               System.out.printf("Imprime somente se o item atual for 'laranja': %s\n", frutas[i]);

           if (i == length - 1)
               System.out.printf("Imprime somente na última iteração do loop: %d\n", i);
       }
    }
}

Creio que assim fique mais claro agora.

Digamos que o usuário seja um lojista.
O algoritmo lê o nome de 100 produtos, seus respectivos valores de compra e venda e calcula o lucro obtido com cada produto. Ademais, informa os produtos que geraram lucros menores que 10%; entre 10% e 20%; e maiores que 20%.
Como é possível observar, imprimi a informação sobre qual percentual de produto será exibido abaixo.
O problema de deixar a informação fora do laço é que, mesmo que, por exemplo, só haja lucros abaixo de 10%, as outras informações – como lucros entre 10 e 20 e acima de 20% – continuarão a serem impressas.

import java.util.Scanner;

public class EX5 {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int size  = 100;
    int i;
    int j;
    double tp;
    double buy[] = new double[size];
    double sal[] = new double[size];
    double luc[] = new double[size];
    String pro[] = new String[size];
    for(i=0; i<size; i++) {
      System.out.print("\nProduto "+(i)+": ");
      pro[i] = sc.next ();
      System.out.print("Valor de compra: R$");
      buy[i] = sc.nextDouble ();
      System.out.print("Valor de venda: R$");
      sal[i] = sc.nextDouble ();
    }
    System.out.println("\nProdutos que ensejam MENOS de 10% de lucro:");
    for(i=0; i<size; i++) {
      tp = ((sal[i]*100/buy[i])-100);
      if(tp < 10) {
        System.out.printf(pro[i]+", %.2f%% de lucro.\n", tp);
        luc[i] = tp;
      }
    }
    System.out.println("\nProdutos que ensejam ENTRE 10% e 20% de lucro:");
    for(i=0; i<size; i++) {
      tp = ((sal[i]*100/buy[i])-100);
      if(tp >= 10 && tp <= 20) {
        System.out.printf(pro[i]+", %.2f%% de lucro.\n", tp);
        luc[i] = tp;
      }
    }
    System.out.println("\nProdutos que ensejam MAIS de 20% de lucro:");
    for(i=0; i<size; i++) {
      tp = ((sal[i]*100/buy[i])-100);
      if(tp > 20) {
        System.out.printf(pro[i]+", %.2f%% de lucro.\n", tp);
        luc[i] = tp;
      }
    }
  }
}

Bom, basicamente se você quer esse comportamento você precisa de algo que te diga se você contem ou não algum valor em um desses grupos. Aí imprime somente os grupos que possuem valores populados.

O que eu faria seria uma modelagem onde poderia manter 3 listas separadas, uma para cada grupo. Uma versão simplificada ficaria assim:

package tests;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

class Produto {

    private String nome;
    private double valorCompra;
    private double valorVenda;
    private double lucro;
    private int porcentagemDeLucro;

    public Produto(String nome, double valorCompra, double valorVenda) {
        this.nome = nome;
        this.valorCompra = valorCompra;
        this.valorVenda = valorVenda;
        this.lucro = valorVenda - valorCompra;
        this.porcentagemDeLucro = calcularPorcentagemDeLucro(valorCompra, valorVenda);
    }

    private int calcularPorcentagemDeLucro(double valorCompra, double valorVenda) {
        return (int)(valorVenda * 100 / valorCompra) - 100;
    }

    public String getNome() {
        return nome;
    }

    public double getValorCompra() {
        return valorCompra;
    }

    public double getValorVenda() {
        return valorVenda;
    }

    public double getLucro() {
        return lucro;
    }

    public int getPorcentagemDeLucro() {
        return porcentagemDeLucro;
    }

    @Override
    public String toString() {
        return String.format("%s rendeu %d%% de lucro", nome, porcentagemDeLucro);
    }
}

public class Ex5 {

    private static List<Produto> menosDe10 = new ArrayList<>();
    private static List<Produto> entre10e20 = new ArrayList<>();
    private static List<Produto> maisDe20 = new ArrayList<>();

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int indiceProduto = 1;
        while (indiceProduto <= 100) {
            System.out.print("\nProduto "+(indiceProduto)+": ");
            String nomeProduto = scanner.next();
            System.out.print("Valor de compra: R$");
            double valorCompra = scanner.nextDouble();
            System.out.print("Valor de venda: R$");
            double valorVenda = scanner.nextDouble ();
            adicionarProdutoNaLista(new Produto(nomeProduto, valorCompra, valorVenda));

            indiceProduto++;
        }

        imprimirRelacaoDeLucro();
    }

    private static void adicionarProdutoNaLista(Produto produto) {
        if (produto.getPorcentagemDeLucro() < 10) {
            menosDe10.add(produto);
        } else if (produto.getPorcentagemDeLucro() <= 20) {
            entre10e20.add(produto);
        } else {
            maisDe20.add(produto);
        }
    }

    private static void imprimirRelacaoDeLucro() {
        if (!menosDe10.isEmpty()) {
            System.out.println("\nProdutos que renderam MENOS de 10% de lucro:");
            menosDe10.forEach(System.out::println);
        }

        if (!entre10e20.isEmpty()) {
            System.out.println("\nProdutos que renderam ENTRE 10% e 20% de lucro:");
            entre10e20.forEach(System.out::println);
        }

        if (!maisDe20.isEmpty()) {
            System.out.println("\nProdutos que renderam MAIS de 20% de lucro:");
            maisDe20.forEach(System.out::println);
        }
    }
}
1 Curtida

@joaostm na minha opinião a sua solução foi a melhor possível pro problema que vc apresentou.

Pra melhorar vc teria que dar um passo além e começar a representar as entidades do seu programa como classes e começar a tirar proveito da biblioteca padrão do Java, usando ArrayList, por exemplo, que é como um array no qual vc pode acrescentar itens indefinidamente.

O exemplo do @Rodrigo_Sasaki é excelente pra vc ver isso em prática, viu como ele criou uma classe só pra representar um produto?

Isso já seria o inicio da programação orientada a objetos.

Também fiz a minha versão pra mostrar uma abordagem diferente tirando proveito de recursos que foram adicionados a partir da versão 8 do Java, veja como ficou:

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining;

import static java.util.List.of;

class Produto {
    String nome;
    double compra;
    double venda;

    Produto(String nome, double compra, double venda) {
        this.nome = nome;
        this.compra = compra;
        this.venda = venda;
    }

    double getLucro() {
        return (venda * 100 / compra) - 100;
    }

    @Override
    public String toString() {
        return String.format("Nome: %s Compra: %.2f Venda: %.2f Lucro: %.2f", nome, compra, venda, getLucro());
    }
}

public class Main {
    public static void main(String[] args) {
        var produtos = of(
                new Produto("AAA", 100, 105),
                new Produto("BBB", 100, 115),
                new Produto("CCC", 100, 150),
                new Produto("DDD", 100, 106),
                new Produto("EEE", 100, 119),
                new Produto("FFF", 100, 140)
        );

        var dados = produtos.stream().collect(groupingBy((produto) -> {
            if (produto.getLucro() < 10) return "Menor que 10%";
            else if (produto.getLucro() <= 20) return "De 10% à 20%";
            else return "Maior que 20%";
        }));

        dados.forEach((lucro, prod) ->
                System.out.printf("Lista de produtos com lucro %s:\n%s\n\n", lucro,
                        prod.stream().map(Produto::toString).collect(joining("\n"))));
    }
}

Gostaria de deixar algumas observações:

#1 No Java, por convenção, ao declarar um array, os colchetes devem vir logo após o tipo e antes do nome.

int[] meuArray;

Da forma como vc fez não tá errado tecnicamente, mas na comunidade Java convencionou-se usar como mostrei.

Só pra não ficar só nas minhas palavras, eis o que diz um trecho do tutorial da Oracle (Tá no final da seção Declaring a Variable to Refer to an Array ^^):

You can also place the brackets after the array’s name:

// this form is discouraged
float anArrayOfFloats[];

However, convention discourages this form; the brackets identify the array type and should appear with the type designation.

#2 Evite declarar a variável de controle do for fora do for em que ela é usada, declare-a sempre no proprio for mesmo que pareça desperdício e que pareça lógico que todos compartilhem a mesma variável.

Então, ao invés de fazer isso:

int i;
for(i = 0; i < 10; i++) { /* ... */ }
for(i = 0; i < 10; i++) { /* ... */ }
for(i = 0; i < 10; i++) { /* ... */ }

Faça isso:

for(int i = 0; i < 10; i++) { /* ... */ }
for(int i = 0; i < 10; i++) { /* ... */ }
for(int i = 0; i < 10; i++) { /* ... */ }

Quanto menor o escopo de uma variável, menos propenso a falhas seu programa será.

#3 Vc está usando o printf que é bem legal, mas vc poderia tirar maior proveito dele.

Vc escreveu assim:

System.out.printf(pro[i] + ", %.2f%% de lucro.\n", tp);

Mas poderia ter feito assim:

System.out.printf("%s, %.2f%% de lucro.\n", pro[i], tp);

É só um detalhe, mas eu acho que faz diferença na legibilidade do código.

@wldomiciano Agradeço as dicas!
Iniciei meus estudos em JAVA há menos de 1 mês. Apesar de ter vindo de um ensino em C um tanto quanto deficiente, tento aproveitar alguns recursos que são presentes lá e podem ser úteis aqui, em JAVA. Ainda tenho muito o que aprender, e praticar!

Novamente, obrigado pelos informes. Serão-me muito usuáis.

1 Curtida