Пользовательские структуры управления в Scala?

Несколько раз я сталкивался с простым паттерном при программировании на Java или C++, для которого пользовательская структура управления могла уменьшить объем шаблонного кода в моем коде. Это выглядит примерно так:

 if( Predicate ){
     Action

     return Value
 }

то есть оператор типа «возврат, если». Я пробовал создавать функции с сигнатурой вроде foo[A,B]( pred:((A,A)=>Boolean), value:Option[B] ), но потом я проверял, вернул ли я Some или None. Я сбит с толку заявлением return.

Есть ли унаследованный способ создания таких управляющих структур в функциональных языках или, в частности, в Scala?

Изменить:

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

def intersect( geometry:Geometry, reference:Geometry ):Geometry = {
    return_if( withinBounds( geometry, projection ), logToString( logger, "Geometry outside " + projection.toString ), EmptyGeometry() )
    return_if( topologicallyCorrect( geometry ), intersect( correct( geometry ), reference )
    //rest of the function
}

и по-прежнему разрешать хвостовую рекурсию в return_if.


person wheaties    schedule 26.08.2010    source источник
comment
Возможно как-то получить такое поведение с помощью продолжений (CPS встроен в 2.8, но должен быть активирован переключателем компилятора), но я недостаточно знаю об этом.   -  person Landei    schedule 27.08.2010


Ответы (4)


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

Итак, в вашем примере он должен выйти из метода пересечения?

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

Надеюсь, я понял, что вы хотели сделать :)

person Plankalkül    schedule 26.08.2010
comment
Да, ты прав. Я даже не думал об этом. Если return_if был передан return_if, это могло вызвать проблемы. - person wheaties; 26.08.2010
comment
Чтобы добиться этого, нужно генерировать исключение, которое затем обрабатывается внешним методом — посмотрите, как реализован break. Вместо return вы бы вызвали метод (например, break), который выдал бы исключение - person oxbow_lakes; 27.08.2010

Я бы использовал частичную функцию:

def whatevs[A, B](options : PartialFunction[(A,A), B]) : Option[B] = {
  val myInput = generateInput
  if(options.isDefined(myInput)) {
    Some(options(myInput))
  } else None
}

Тогда ваше использование может выглядеть следующим образом:

whateves {
   case (x,y) if x > y =>   "Biggerz"
   case (x,y) if y > x =>   "Tiny!"
}

В общем случае вам не нужен оператор return. If-expression будет оценивать последнее выражение, использованное в каждом блоке. Возможно, вам придется помочь компилятору определить тип результата выражения if, но возврат не нужен.

Частичные функции — это механизм для выполнения действия, если выполняется какое-либо условие. В приведенном выше примере два условия: x > y или y > x из кортежа.

Если функция whatevs не совсем то, о чем вы говорите, я бы рекомендовал использовать необработанное сопоставление с образцом.

person jsuereth    schedule 26.08.2010

Похоже, вы используете это как условный выход из потока управления кодом.

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

Конечно, вы всегда можете вложить операторы if, но это неудобно.

Однако в Scala есть еще несколько вещей, которые следует учитывать. Можно

methodReturningOption.getOrElse(
  // All the code for the None case goes in here
)

который обычно довольно хорошо работает при объединении разных ветвей в случае разумного значения по умолчанию (или вы в конечном итоге сгенерируете исключение, если его нет).

В качестве альтернативы вы можете злоупотреблять сопоставлением с образцом для этого:

None match {  // Could use a real variable instead of None if it helped
  case _ if (Predicate1) => Action1; Value1
  case _ if (Predicate2) => Action2; Value2
  . . .
  case _ => ErrorHandlingPerhaps
}

Но вы также можете подумать о своей проблеме по-другому, чтобы подобные предикаты стали менее полезными. (Не зная подробностей, я не могу что-то предложить.)

person Rex Kerr    schedule 26.08.2010

Я затрудняюсь понять, зачем вам это нужно. Вот код, который вы хотите написать:

def intersect( geometry:Geometry, reference:Geometry ):Geometry = {

  return_if( withinBounds( geometry, projection ), 
    logToString( logger, "Geometry outside " + projection.toString ), 
    EmptyGeometry() )

  return_if( topologicallyCorrect( geometry ), 
    intersect( correct( geometry )), 
    reference )

    // rest of the function
}

а вот как это выглядит в «обычном» Scala:

def intersect( geometry:Geometry, reference:Geometry ):Geometry = {

  if (withinBounds( geometry, projection )) {
    logToString( logger, "Geometry outside " + projection.toString )
    return EmptyGeometry() }

  if( topologicallyCorrect( geometry )) {
    intersect( correct( geometry ))
    return reference }

  //rest of the function
}

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

person Magnus    schedule 26.08.2010