Pętla FOR - znalazłem buga czy to tak ma być?

0

Cześć!

Do wykonania miałem proste zadanie ze ślimakiem, który w ciągu dnia wychodzi po słupie, a w nocy zjeżdża o określoną wartość.
Należy obliczyć ilość dni których potrzebuje ślimak do wejścia na słup.
Być może chciałem to zrobić zbyt bardzo na skróty i jakość tego kodu nie powala, jednakże prosiłbym o wyjaśnienie.

Dlaczego, pomimo spełnienia warunku FALSE, przy 8 okrążeniu pętli dokonuje się inkrementacja? W sumie result powinien wynosić 8 a nie 10,

public class Main {

    public static void main(String[] args) {

        int h = 10; //Wysokość słupa
        int a = 3; //Tyle ślimak wychodzi w ciągu dnia
        int b = 2; //Tyle ślimak zjeżdża w nocy
        int result = 0;

        for(; h > 0; h += b) {
            h -= a;
            result++;
        }
        System.out.println(result);
}
}

Dzięki z góry za wyjaśnienie,
Artur

2
  1. Logika to tu nie powala, bo sugerowałaby że zaczynasz od h=0 i przerywasz kiedy h==10 a ty robisz jakieś cuda na kiju
  2. Nie bardzo rozumiem twój problem. Skoro wchodzi o 3 a zjeżdża o 2 to znaczy że każdego dnia wysokość zmienia się sumarycznie o 1. Skoro słup ma 10 to siłą rzeczy trzeba 10 dni. Weź pod uwagę że warunek pętli jest ewaluowany RAZ na obieg pętli a nie że magicznie ja przeywa kiedykolwiek kiedy wartość zmiennej nie spełniłaby warunku.
for(int i=0;i<10;i++){
    int x = i;
    i = 100; // pętla wcale sie nie przerywa bo warunek będzie sprawdziony dopiero przy nowym obiegu!
    System.out.println("To sie wypisze");
    i = x;
}
2
Shalom napisał(a):
  1. Nie bardzo rozumiem twój problem. Skoro wchodzi o 3 a zjeżdża o 2 to znaczy że każdego dnia wysokość zmienia się sumarycznie o 1. Skoro słup ma 10 to siłą rzeczy trzeba 10 dni.

Jest błąd w pętli i pętla działa według logiki @Shalom.
@Shalom, @Op zauważcie że jak ślimak wszedł w dzień to już w nocy nie zjedzie więc z każdego dnia wchodzi 1m za wyjątkiem ostatniego gdzie wchodzi trzy metry

brzydka poprawka na szybko:

 for(; h > 0; h += b) {
            h -= a;
            result++;
            if (h > 0) break;
        }, 

Ale to powinno być napisane inaczej, może:

 h -= a;
 result =+ 1;
 while(h > 0) {
      h += b
      h -= a;
      result =+ 1;
 } 
2
for(A;B;C){
    D
}

A - wykona się raz przed całą pętlą
B - wykona się przed każdym obiegiem pętli i pętla wykona kolejny obieg tylko jeśli B zwróci true
C - wykona się po każdym obiegu pętli
D - wykona się w każdym obiegu pętli jeśli B było true, wykona się przed C

0

Teraz już rozumiem. Przyjąłem, że pętla zawsze sprawdza B zanim wykona C (przy drugim i kolejnym przebiegu).
W dodatku debuger utwierdzał mnie w tym przekonaniu, pewnie źle go zinterpretowałem

Dzięki za pomoc!

0

Zamiast zgadywać warto sie zastanowić jak to sie implementuje:

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }

Kompiluje się do:

  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 3 L0
    ICONST_0
    ISTORE 1
   L1
   FRAME APPEND [I]
    ILOAD 1
    BIPUSH 10
    IF_ICMPGE L2
   L3
    LINENUMBER 4 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ILOAD 1
    INVOKEVIRTUAL java/io/PrintStream.println (I)V
   L4
    LINENUMBER 3 L4
    IINC 1 1
    GOTO L1
   L2
    LINENUMBER 6 L2
   FRAME CHOP 1
    RETURN
   L5
    LOCALVARIABLE i I L1 L2 1
    LOCALVARIABLE args [Ljava/lang/String; L0 L5 0
    MAXSTACK = 2
    MAXLOCALS = 2
}

Interesuje nas kawałek między L1 i L2. Zobacz że mamy:

   L1
   FRAME APPEND [I]
    ILOAD 1
    BIPUSH 10
    IF_ICMPGE L2

Czyli ładujemy pierwszą zmienną lokalną, porównujemy z 10 i jak jest greater or equal to skaczemy do L2 czyli za pętle.
Potem mamy tego naszego printa, a dopiero po nim:

    IINC 1 1
    GOTO L1
   L2

Czyli na sam koniec pętli jest inkrementacja i skok na początek.

1

IMHO naturalne podejście. Usuwanie dopiero rekurencji kiedy jest taka potrzeba

Fibonacci, factorial, naturalne definicja jest rekurencyjna. Fibonacci można prosto napisać rekurencyjnie, memoizatiion (DP), step up z generowaniem tablicy wcześniejszych wyników albo w pętli while.
Rekurencyjne podejście do takich tematów, Fibonacci albo ślimak na słupie jest najbardziej intuicyjne

Najlepiej sobie rozrysować na kartce pierwsze kilka dni, ślimak zaczął od zera, idzie 3 m w górę, nie doszedł do wierzchołka, zjechał w nocy 2 m
Nowy dzień, obudził się 1 m nad ziemią, nie doszedł do wierzchołka do wieczora, idzie spać, zjeżdża 2 m w dół...

Pewnego dnia doszedł na szczyt i tu koniec opowieści.

@Artur Gacek: zadanie nie był ani całkiem proste, ani banalne. IMHO inna wersja fizz-buzz, pokazująca jak programista myśli
I dlatego uważam, że jak jakieś "rozmowy algorytmiczne" to tylko przy "tablicy", a nie zadania domowe an tydzień albo anonimowo codility na czas.
Przy okazji Fibonacci, ile tematów do omówienia przy szerszym sprawdzaniu wiedzy na interview przy okazji prostego zadanka? ;) - Dali proste zadanie ze ślimakiem, w końcu coś napisałem według. mnie rozwiązałem 100%, ale nie pytali więcej i się już nie odezwali... ;)

snail.js

const poleHeight = 10
const goesUp = 3
const goesDown = 2
let currentPosition = 0
let days = 0

function climb() {

    //from down till dusk
    days++
    currentPosition += goesUp;
    if (currentPosition >= poleHeight) {
        return days;
    }

    // from dusk till down
    currentPosition -= goesDown;

    // another day of hard work
    climb()
}

climb()

console.log(days);

Konsola

 node snail
8

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0