Как применить continue и получить несколько значений из цикла for в scala?

#java #scala #short-circuiting

#java #scala #короткое замыкание

Вопрос:

Я пытаюсь преобразовать модуль Java-программы в Scala. До сих пор мне удавалось применять парадигму функционального программирования Scala и ее синтаксис в каждом преобразованном мной модуле. Но я наткнулся на метод, который выполняет некоторые проверки, использует continue и, наконец yield , выводит. Ниже приведен код на Java:

 public boolean checkColumn(String server, String database, String schema, String table, String column) {
        boolean bServer, bDatabase, bSchema, bTable, bColumn, bRet = false;
        for (int i = 0; i < columns.length; i  ) {
            if ((server == null) || (server.length() == 0)) {
                bServer = true;
            } else {
                bServer = columns[i][0].equalsIgnoreCase(server);
            }
            if (!bServer) continue;

            if ((database == null) || (database.length() == 0)) {
                bDatabase = true;
            } else {
                bDatabase = columns[i][1].equalsIgnoreCase(database);
            }
            if (!bDatabase) continue;

            if ((schema == null) || (schema.length() == 0)) {
                bSchema = true;
            } else {
                bSchema = columns[i][2].equalsIgnoreCase(schema);
            }

            if (!bSchema) continue;

            bTable = columns[i][3].equalsIgnoreCase(table);
            if (!bTable) continue;

            bColumn = columns[i][4].equalsIgnoreCase(column);
            if (!bColumn) continue;

            bRet = true;
            break;
        }
        return bRet;
    }
  

Хотя я понимаю continue , что в последних версиях Scala этого нет, я могу понять, как написать тот же код в Scala. Я попытался создать цикл for, как показано ниже:

 val finalReturn = for {i <- 0 until columns.length
    
} yield bRet
  

Но не мог придумать способ сформировать логику всех условий if amp; continue внутри цикла for. Может кто-нибудь сообщить мне, как я могу написать тот же код в Scala?

Ответ №1:

Вы проверяете, что (по крайней мере) один из columns тестов соответствует всем тестам. Это exists операция:

 def checkColumn(server: String, database: String, schema: String, table: String, column: String) = {
  columns.exists { col =>
    (server.isEmpty || col(0).equalsIgnoreCase(server)) amp;amp;
    (database.isEmpty || col(1).equalsIgnoreCase(database)) amp;amp;
    (schema.isEmpty || col(2).equalsIgnoreCase(schema)) amp;amp;
    col(3).equalsIgnoreCase(table) amp;amp;
    col(4).equalsIgnoreCase(column)
  }
  

Это будет проверять каждый элемент по очереди, пока один из столбцов не пройдет все тесты (которые вернутся true ) или список не будет исчерпан, который вернется false .

Комментарии:

1. Так намного чище.

Ответ №2:

Невозможно написать точно такой же алгоритм с функциональным подходом, потому что в коде Java это очень императивный стиль с изменчивостью и ручным управлением потоком вычислений. Итак, вы должны подумать, что делает этот код, какова логика в коде. А затем реализовать эту логику с помощью функциональных примитивов и шаблонов.

Давайте пойдем шаг за шагом

         if ((server == null) || (server.length() == 0)) {
            bServer = true;
        } else {
            bServer = columns[i][0].equalsIgnoreCase(server);
        }
        if (!bServer) continue;
  

он проверяет, есть ли определенная строка сервера — вы должны проверить, что 1-е поле столбца
должно быть равно этой определенной строке сервера (я не знаю, что columns именно, но я пытаюсь догадаться, что columns[i] это column и column[j] является полем столбца).
В противном случае он вызывает continue и пропускает итерацию.
То же самое для других полей, за исключением того, что table и schema не проверяется на пустоту.

Если мы посмотрим в конец, пропуск приведет к «не установленному bRet значению true «. Итак, если все проверки пройдены для некоторого столбца, bRet будет true, и после этого цикл прерывается.

Итак, мы можем сказать, что «если существует хотя бы один столбец, который проходит проверки — результат этого метода должен быть true». Хорошо, что в scala у вас есть специальный метод сбора exists данных именно с такой логикой.

Кроме того, лучше иметь специальную вспомогательную функцию для решения проблем с обнуляемостью и пустотой.

 private def isEmpty(string: String) = string == null || string.isEmpty

def checkColumn(server: String, database: String, schema: String, table: String, column: String): Boolean = {
  columns.exists { column =>
      (isEmpty(server)   || column(0).equalsIgnoreCase(server)) amp;amp;
      (isEmpty(database) || column(1).equalsIgnoreCase(database)) amp;amp;
      (isEmpty(schema)   || column(2).equalsIgnoreCase(schema)) amp;amp;
                            column(3).equalsIgnoreCase(table) amp;amp;
                            column(4).equalsIgnoreCase(column)
  }
}
  

Кроме того, я должен упомянуть, что в Scala работа с нулями в бизнес-коде является очень плохой практикой, вам следует изменить параметры с нулевым Option[String] значением и немного изменить выражения, чтобы иметь дело с этим типом. Я покажу 3 способа сделать это:

 def checkColumn(serverOpt: Option[String], databaseOpt: Option[String], schemaOpt: Option[String], String table, String column): Boolean =
  columns.exists { column =>
      serverOpt.filterNot(_.isEmpty).map(server => column(0).equalsIgnoreCase(server).getOrElse(true) amp;amp;
      databaseOpt.filterNot(_.isEmpty).map(column(1).equalsIgnoreCase).getOrElse(true) amp;amp;
      schemaOpt.filterNot(_.isEmpty).fold(true)(column(2).equalsIgnoreCase)amp;amp;
      column(3).equalsIgnoreCase(table) amp;amp;
      column(4).equalsIgnoreCase(column)
  }
  

И когда вы его вызываете, вы должны обернуть свои строки с нулевым Option значением с помощью конструктора, подобного этому:

 checkColumn(Option(nullableServer), Option(nullableDatabase), Option(nullableshema), table, column)
  

Дальнейшими улучшениями было бы использование методов уточнения типов и изменение типа nullable и, возможно, пустых строк с just Option[String] на Option[NonEmptyString] .