2016-05-03 5 views
1

Я только начал изучать erlang (и функциональное программирование), и я застрял в простой программе. Объект программы - найти наибольший простой коэффициент числа. Это моя программа:Отладка простой программы в Erlang

lprime(N, L, D) when D == N-> 
    if N rem D == 0 -> D; 
     true-> L 
    end; 
lprime(N,L,D) -> 
    if N rem D == 0 -> 
     lprime(N/D, D, D); 
     true -> lprime(N, L, D+1) 
    end. 
lprime(N)-> 
    lprime(N,1,2). 

Вот как он должен работать для некоторых входов:

lprime(3)->lprime(3,1,2)->lprime(3,1,3)->3 
lprime(36)->lprime(36,1,2)->lprime(18,2,2)->lprime(9,2,2)->lprime(9,2,3)->lprime(3,3,3)->3 
lprime(14)->lprime(14,1,2)->lprime(7,2,2)->lprime(7,2,3)->lprime(7,2,4)->lprime(7,2,5)->lprime(7,2,6)->lprime(7,1,7)->7 

Но программа всегда возвращает первый простой делитель вместо. lprime(24)->2, lprime(9)->3

Я написал эквивалент (на мой взгляд) программы в Python, которые я больше знакомы с этим выполняет точно так, как ожидалось:

def lprime(N, L=1, D=2): 
    if D==N: 
     if N%D == 0: return D 
     return L 
    if N%D == 0: 
     return lprime(N/D, D, D) 
    return lprime(N, L, D+1) 

Я также попробовал другую версию без охраны (он выглядит чище, тоже) но это, кажется, идти в бесконечную рекурсию, снова питон эквивалент (ИМО) работает, как ожидалось:

lprime2(1, L, D) -> 
    L; 
lprime2(N,L,D) -> 
    if N rem D == 0 -> 
     lprime2(N/D, D, D); 
     true -> lprime2(N, L, D+1) 
    end. 
lprime2(N)-> 
    lprime2(N,1,2). 

Я пытался отладить программу с помощью DBG, документация которых очень скудны, и я не» Понимать шаги ver хорошо. Эти шаги, которые я использовал, были:

1> dbg:start(). 
{ok,<0.35.0>} 
2> dbg:tracer(). 
{ok,<0.35.0>} 
3> dbg:tp(first,lprime, 1, []). 
{ok,[{matched,[email protected],1}]} 
4> dbg:tp(first,lprime,3,[]). 
{ok,[{matched,[email protected],1}]} 
5> dbg:p(all,c). 
{ok,[{matched,[email protected],26}]} 
6> first:lprime(10). 
(<0.33.0>) call first:lprime(10) 
2 
7> first:lprime(10,1,2). 
(<0.33.0>) call first:lprime(10,1,2) 

Edit: Выделено
я не нашел никакой полезной информации из этого и я бы признателен за любые указатели о том, как отлаживать эффективно тоже, но основном я бы как знать, что заставляет программу терпеть неудачу.

+0

Возможный дубликат [Использование трассировки и dbg в Erlang] (http://stackoverflow.com/questions/1954894/using-trace-and-dbg-in-erlang) –

+0

@SteveVinoski Использование trace и dbg - это моя второстепенная проблема , в первую очередь я хочу знать, почему эта программа терпит неудачу. –

+0

другой инструмент - приложение отладчика: debugger: start() – Pascal

ответ

2

Вы используете деление с плавающей запятой вместо целочисленного деления, и это вызывает исключения в rem. Но вы не видите эти исключения, потому что вы вызываете rem в охрану.Вы можете увидеть это с помощью case, а не if:

lprime2(1, L, D) -> 
    L; 
lprime2(N,L,D) -> 
    case N rem D of 
     0 -> lprime2(N/D, D, D); 
     _ -> lprime2(N, L, D+1) 
    end. 
lprime2(N)-> 
    lprime2(N,1,2). 

Это позволит вам увидеть исключение:

1> c(lp). 
lp.erl:4: Warning: variable 'D' is unused 
{ok,lp} 
2> lp:lprime2(14). 
** exception error: an error occurred when evaluating an arithmetic expression 
    in function lp:lprime2/3 (/tmp/lp.erl, line 7) 

Чтобы исправить это, используйте div, а не / в вашем втором пункте о lprime/3:

lprime2(N,L,D) -> 
     case N rem D of 
      0 -> lprime2(N div D, D, D); 
      _ -> lprime2(N, L, D+1) 
     end. 

В целом, идиоматический код Erlang использует case more th if, потому что последний допускает в своих предложениях только охранники.

Еще одна вещь, следует отметить, что в вашем коде с охранниками на функциональных пунктах (а также в коде Python), когда N == D, то N rem D всегда будет правдой, так что вы можете упростить код:

lprime(N,_,N) -> 
    N; 
lprime(N,_,D) when N rem D == 0 -> 
    lprime(N div D, D, D); 
lprime(N,L,D) -> 
    lprime(N, L, D+1). 

В первом предложении мы используем ту же переменную N как для аргументов N10, так и для D. Этот пункт работает только тогда, когда N и D равны. В этом случае нет необходимости в тесте rem.

+0

Спасибо. Практически на каждом языке я использовал '/' по умолчанию для целочисленного деления, а float должен быть отличен. И разъяснение if/case наиболее полезно. Я использовал «Learn you some Erlang» в качестве ссылки, и он специально представил «if» как «what if», и теперь я знаю почему. –

1

Ошибка при портировании кода заключается в том, что в Python, 5/2 == 2 в то время как в Эрланге, 5/2 == 2.5. Вы должны использовать div оператор: 5 div 2 == 2

1> 5/2. 
2.5 
2> 5 div 2. 
2 

Таким образом, в вашем коде замените:

lprime(N/D, D, D); 

с:

lprime(N div D, D, D); 

С этим изменением, я получаю ожидаемые результаты:

2> a:lprime(3). 
3 
3> a:lprime(36). 
3 
4> a:lprime(14). 
7 

На стороне записки о вашей логике, я уверен, что если N == D, N rem D всегда будет 0, так что вы можете захотеть упростить код там.

Смежные вопросы