【问题标题】:How to apply continue and yield multiple values from a for-loop in scala?如何在scala中的for循环中应用continue并产生多个值?
【发布时间】:2021-01-05 10:58:48
【问题描述】:

我正在尝试将 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;
    }

虽然我知道最近版本的 Scala 中没有 continue,但我能够理解如何在 Scala 中编写相同的代码。我尝试构建一个for循环如下:

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

但是想不出一种方法来在 for 循环中形成所有 if 条件和 continue 的逻辑。谁能告诉我如何在 Scala 中编写相同的代码?

【问题讨论】:

    标签: java scala short-circuiting


    【解决方案1】:

    您正在检查(至少)columns 之一与所有测试匹配。这是exists 操作:

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

    这将依次检查每个元素,直到其中一列通过所有测试(将返回true)或列表用完,将返回false

    【讨论】:

    • 干净多了。
    【解决方案2】:

    没有办法用函数式方法编写完全相同的算法,因为在 java 代码中,这是一种非常命令式的风格,具有可变性和对评估流程的手动控制。 所以你应该想想这段代码做了什么,代码中的逻辑是什么。然后用功能原语和模式来实现这个逻辑。

    让我们一步一步来

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

    它检查是否存在已定义的服务器字符串 - 您应该检查该列的第一个字段 应该等于定义的服务器字符串(我不知道 columns 到底是什么,但我试着猜测 columns[i]columncolumn[j] 是列的一个字段)。 否则它会调用 continue 并跳过迭代。 除了不检查 tableschema 是否为空之外,其他字段相同。

    如果我们查看结尾,跳过会导致“未将 bRet 设置为 true”。因此,如果某个列的所有检查都通过了,bRet 将为真,然后循环中断。

    所以我们可以说“如果存在至少一列通过检查 - 该方法的结果应该为真”。对你有好处的是,在 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)) &&
          (isEmpty(database) || column(1).equalsIgnoreCase(database)) &&
          (isEmpty(schema)   || column(2).equalsIgnoreCase(schema)) &&
                                column(3).equalsIgnoreCase(table) &&
                                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) &&
          databaseOpt.filterNot(_.isEmpty).map(column(1).equalsIgnoreCase).getOrElse(true) &&
          schemaOpt.filterNot(_.isEmpty).fold(true)(column(2).equalsIgnoreCase)&&
          column(3).equalsIgnoreCase(table) &&
          column(4).equalsIgnoreCase(column)
      }
    

    当你调用它时,你应该用 Option 构造函数包装你的可为空字符串,如下所示:

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

    进一步的改进是使用细化类型技术,并将可空字符串和可能为空的字符串的类型从 Option[String] 更改为 Option[NonEmptyString]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-07
      • 1970-01-01
      • 2019-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-05
      相关资源
      最近更新 更多