F#将两个阵列的等效性与指定的精度水平进行比较

时间:2011-03-07 03:59:35

标签: arrays f# multidimensional-array precision

以下是我要翻译的C#代码:

public bool equals(Matrix matrix, int precision)
    {

        if (precision < 0)
        {
            throw new MatrixError("Precision can't be a negative number.");
        }

        double test = Math.Pow(10.0, precision);
        if (double.IsInfinity(test) || (test > long.MaxValue))
        {
            throw new MatrixError("Precision of " + precision
                    + " decimal places is not supported.");
        }

        precision = (int)Math.Pow(10, precision);

        for (int r = 0; r < this.Rows; r++)
        {
            for (int c = 0; c < this.Cols; c++)
            {
                if ((long)(this[r, c] * precision) != (long)(matrix[r, c] * precision))
                {
                    return false;
                }
            }
        }

        return true;
    }
  

这是我到目前为止所做的:

type Matrix(sourceMatrix:double[,]) =
     let rows = sourceMatrix.GetUpperBound(0) + 1
     let cols = sourceMatrix.GetUpperBound(1) + 1
     let matrix = Array2D.zeroCreate<double> rows cols
     do
      for i in 0 .. rows - 1 do
      for j in 0 .. cols - 1 do
        matrix.[i,j] <- sourceMatrix.[i,j]
    ///The number of Rows in this Matrix.
    member this.Rows = rows

    ///The number of Columns in this Matrix.
    member this.Cols = cols

    member this.Equals(matrix:Matrix, precision:int) =
     if(precision < 0) then raise (new ArgumentOutOfRangeException("Precision can't be a negative number."))
     let (test:double) = Math.Pow(10.0, double(precision))
     if(System.Double.IsInfinity(test) || (test > double(System.Int32.MaxValue))) then raise (new ArgumentOutOfRangeException("Precision of " + precision.ToString() + " decimal places is not supported."))
     let precision = int(Math.Pow(10.0, double(precision)))

正如你所看到的,我到目前为止所编写的内容中都加载了类型转换,这可能意味着我的代码没有按照应有的方式编写。未完成的部分需要该方法返回第一个元素,该元素在求值到某个精度时返回false。我敢肯定必须有一些优雅的F#代码来实现这一点,显然我无处可去。我试图弄清楚Array2D类是否有一些方法可以让我这样做,但是如果有的话我无法找到它。我知道PowerPack Matrix类并且最终会使用它,但是现在我试图通过将我理解的C#代码翻译成F#来学习F#。说起来容易做起来容易做起来难。 :)我相信我已经在我正在创建的类型中添加了所有相关的F#代码。如果我遗失了什么,请告诉我。

2 个答案:

答案 0 :(得分:4)

一种优雅而高级的方式来编写这可能不会非常有效的方法是使用延迟序列表达式:

seq { for r in 0 .. this.Rows - 1 do
        for c in 0 .. this.Cols - 1 do
          if <your condition goes here> then
            yield false}
|> Seq.forall id

我们的想法是,只要矩阵中的第一个元素与条件匹配,序列就会生成false。然后Seq.forall函数立即返回false(并停止迭代序列)。

在实践中,您可能需要使用递归函数来实现它,以使其高效。这不是特别好(因为在F#中无法完成循环),但是你不应该经常需要这样的代码:

let rec loopRows r = 
  let rec loopCols c = 
    if c = this.Cols then true
    elif <your condition goes here> then false
    else loopCols (c + 1)
  if r = this.Rows then true       // Processed all rows
  elif not (loopCols 0) then false // Nonequal element in this row
  else loopRows (r + 1)            // Continue looping

loopRows 0

答案 1 :(得分:2)

这是您的代码的自由翻译:

type Matrix(sourceMatrix) =
    let rows = Array2D.length1 sourceMatrix
    let cols = Array2D.length2 sourceMatrix
    let matrix = Array2D.copy sourceMatrix

    ///The number of Rows in this Matrix.
    member this.Rows = rows

    ///The number of Columns in this Matrix.
    member this.Cols = cols

    ///Retrieve data from this Matrix
    member this.Item(x,y) = matrix.[x,y]

    member this.Equals(matrix:Matrix, precision) =
        if precision < 0 then failwith "Precision can't be a negative number."
        let precision = 10.0 ** double(precision)
        if Double.IsInfinity(precision) then failwith ("Precision of " + string(precision) + " decimal places is not supported.")
        seq {
            for r in 0 .. rows - 1 do
                for c in 0 .. cols - 1 do
                    if floor(matrix.[r, c] * precision) <>
                       floor(this.[r, c] * precision) then
                        yield false
        } |> Seq.forall id

注意我是如何将数组方法调用更改为Array2D函数调用的,这些函数调用了一些东西。然后我添加了一个索引器(this.Item),以便您可以实际读取矩阵中的数据。

您会注意到我通过调用floor将转换的使用更改为整数,从而消除了在整数和浮点数之间进行转换的大量需求。

比较是在托马斯建议的懒惰序列内完成的。