F # Сравнение двух массивов на равенство с заданным уровнем точности - PullRequest
1 голос
/ 07 марта 2011

Вот код 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 и буду использовать его в конце концов, но сейчас я пытаюсь выучить F #, переводя код C #, который я понимаю, в F #. Легче сказать, чем сделать, по-видимому. :) Я считаю, что я добавил весь соответствующий код F # в тип, который я создаю. Дайте мне знать, если я что-то упустил.

Ответы [ 2 ]

4 голосов
/ 07 марта 2011

Элегантный и высокоуровневый способ написать это, который, вероятно, не будет чрезвычайно эффективным, - это использовать ленивые выражения последовательности:

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
2 голосов
/ 07 марта 2011

Вот либеральный перевод вашего кода:

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, что устраняет многоо необходимости конвертации между типами int и float.

Сравнение выполняется внутри ленивой последовательности, как предложил Томас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...