2015-02-23 2 views
4

Я очень новичок в Erlang. Я пытался выяснить, если индекс списка находится вне границ (прежде чем пытаться его), так что я хотел сделать, если положение с чем-то вродеErlang: вложенные случаи

if lists:flatlength(A) < DestinationIndex .... 

Я обнаружил, что эти результаты функции не могут быть использованы, если охранники так я вместо этого используется. Это приводит к заявлению вложенных случаев

case Destination < 1 of 
    true -> {ok,NumberOfJumps+1}; 
    false -> 
    case lists:flatlength(A) < Destination of 
     true -> 
      doSomething; 
     false -> 
      case lists:member(Destination,VisitedIndices) of 
       true -> doSomething; 
       false -> 
        doSomethingElse 
      end 
    end 
end. 

Я нашел это плохое с точки зрения удобочитаемости и стиля кода. Это то, как вы делаете такие вещи в erlang или есть более элегантный способ сделать это?

Заранее спасибо

+0

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

ответ

9

Перед тем, как принять следующий как некое магическое Евангелие, обратите внимание, что путь эта функция вводится почти наверняка unidiomatic. Вы должны стремиться ограничивать случаи до того, как вы доберетесь до этого момента - потребность в вложенных случаях сама по себе является запахом кода. Иногда это действительно неизбежно, но я сильно подозреваю, что некоторые аспекты этого могут быть упрощены ранее в коде (особенно с некоторыми соображениями, данными о структурах данных, которые передаются и что они означают).

Не видя, откуда идет переменная A, я делаю ее здесь как параметр. Кроме того, не видя, как эта функция была введена, я составляю функцию, потому что без остальной функции, которую нужно выполнить, довольно сложно сказать что-либо точно.

С учетом всего сказанного, давайте реорганизовать это немного:

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

foo(Destination, NumberOfJumps, _, _) when Destination < 1 -> 
    {ok, NumerOfJumps + 1}; 
foo(Destination, _, VisitedIndices, A) -> 
    case lists:flatlength(A) < Destination of 
     true -> doSomething; 
     false -> 
      case lists:member(Destination,VisitedIndices) of 
       true -> doSomething; 
       false -> 
        doSomethingElse 
      end 
    end. 

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

foo(Destination, NumberOfJumps, _, _) when Destination < 1 -> 
    {ok, NumberOfJumps + 1}; 
foo(Destination, _, VisitedIndices, A) -> 
    ALength = lists:flatlength(A) < Destination, 
    AMember = lists:member(Destionation, VisitedIncides), 
    NextOp = if 
     ALength  -> fun doSomething/0; 
     AMember  -> fun doSomething/0; 
     not AMember -> fun doSomethingElse/0 
    end, 
    NextOp(). 

Здесь я просто вырезать в погоню и убедились, что мы только выполнить каждый потенциально дорогостоящую операцию один раз, присваивая результат переменной - но это делает меня очень неудобным, потому что я не должен быть в этой ситуации для начала.

В любом случае, что-то вроде этого должно протестировать то же, что и предыдущий код, и в промежуточный период может быть более читаемым. Но вы должны искать другие места для упрощения. В частности, этот бизнес чувствует себя подозрительным (почему бы нам не знать, является ли Destination членом?), Переменная A, нуждающаяся в том, чтобы быть сплющенной после того, как мы прибыли в эту функцию, является нечетной (почему она еще не сглажена? есть ли так много?), и NumberOfJumps чувствует что-то вроде аккумулятора, но его присутствие таинственно.

Что заставляет меня чувствовать себя странно об этих переменных, вы можете спросить? Единственный, который постоянно используется, - Destination - остальные используются только в одном пункте foo/4 или другом, но не для обоих.Это заставляет меня думать, что это должны быть разные пути исполнения где-то дальше по цепочке исполнения, а не все, что происходит здесь, в функции супер-решения-о-матичного типа.

EDIT

С полным описанием проблемы в руке (ссылка на обсуждение в комментариях ниже), рассмотрим, как это работает:

-module(jump_calc). 
-export([start/1]). 

start(A) -> 
    Value = jump_calc(A, length(A), 1, 0, []), 
    io:format("Jumps: ~p~n", [Value]). 

jump_calc(_, Length, Index, Count, _) when Index < 1; Index > Length -> 
    Count; 
jump_calc(Path, Length, Index, Count, Visited) -> 
    NewIndex = Index + lists:nth(Index, Path), 
    NewVisited = [Index | Visited], 
    NewCount = Count + 1, 
    case lists:member(NewIndex, NewVisited) of 
     true -> NewCount; 
     false -> jump_calc(Path, Length, NewIndex, NewCount, NewVisited) 
    end. 

Всегда старайтесь передней нагрузки, как много возможно, вместо того, чтобы выполнять один и тот же расчет снова и снова. Подумайте, как легко мы можем задержать каждую итерацию за охранниками, и сколько условных вещей нам даже не нужно писать из-за этого. Совпадение функций - мощный инструмент - как только вы его повесите, вы действительно начнете наслаждаться Эрланг.

+0

Вау, спасибо за этот подробный ответ. Остальная часть этой программы выглядит так: 'calculate_jumps (A) -> \t calculate_jumps (1, A, 0, []). calculate_jumps (индекс, А, NumberOfJumps, VisitedIndices) -> \t Значение = списки: степени п (индекс, A), \t Направление = Индекс + Значение, ' – StefanG

+0

Функция определяет число скачков, которые могут быть выполнены в список, переходящий из одного элемента в другой, следуя этим правилам: начиная с индекса 0, а затем переходя к элементу с индексом, рассчитанным по сумме старого индекса плюс значение в A [0]. Если A [0] является, например, «3», следующий доступный элемент должен быть A [3]. В списке [1,3, -1,2, -1] должны быть скачки от A [0], A [1], A [4], A [3], A [5], где последний индекс n не существует. функция должна затем вернуть количество прыжков, которые могут быть выполнены, здесь 4. – StefanG

+0

@StefanG Я обновил ответ с помощью игрушечного модуля, основываясь на описании более крупной проблемы. Я не говорю, что это лучший способ сделать это (я не очень внимательно это рассматривал), но он иллюстрирует, как построить барьер (два защищенных предложения * гарантируют *, что «Индекс» находится в «Пути») и совпадение в голове функции может резко сократить процедурный код. Дайте это подумать, пока не начнут загораться, а затем попытайтесь приготовить еще одну проблему для себя, чтобы тренироваться. – zxq9

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