Array.isDefinedAt для n-мерных массивов в scala

#arrays #scala

#массивы #scala

Вопрос:

Есть ли элегантный способ выразить

 val a = Array.fill(2,10) {1}
def do_to_elt(i:Int,j:Int) {
    if (a.isDefinedAt(i) amp;amp; a(i).isDefinedAt(j)) f(a(i)(j))
}
  

в scala?

Ответ №1:

Я рекомендую вам не использовать массивы arrays для 2D-массивов по трем основным причинам. Во-первых, это допускает несогласованность: не все столбцы (или строки, на ваш выбор) должны быть одинакового размера. Во-вторых, это неэффективно — вы должны следовать двум указателям вместо одного. В-третьих, существует очень мало библиотечных функций, которые прозрачно и с пользой работают с массивами массивов в виде 2D-массивов.

Учитывая эти обстоятельства, вам следует либо использовать библиотеку, поддерживающую 2D-массивы, например scalala, либо написать свою собственную. Если вы сделаете последнее, среди прочего, эта проблема волшебным образом исчезнет.

Итак, с точки зрения элегантности: нет, способа нет. Но помимо этого, путь, с которого вы начинаете, содержит много неэлегантности; вероятно, вам было бы лучше поскорее сойти с него.

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

1. Я был полностью уверен, что компиляторы scala волшебным образом преобразуют Array[Массив] в реальный 2D-массив, поскольку он поддерживается стандартной библиотекой ( Array.fill(n,dim,array) ). Какой облом. Разве в стандартной библиотеке нет ничего похожего на 2D-массив? (Вектор[Vector[_]] 😉

2. Что вы подразумеваете под «реальным 2D-массивом»? JVM не имеет встроенной поддержки многомерных массивов.

3. @SethTisue — Я предполагаю, что «реальный 2D-массив» представляет собой матрицу M x N, предположительно такую, где данные хранятся в порядке следования строк, сглаженных в памяти, где размерность является соглашением об индексации, а не отдельными структурами данных. В качестве альтернативы, это может быть массив массивов, размерность которых задана как идентичная (если у вас есть параметр typed size, чего нет в обычных массивах). Ключевое свойство заключается в том, что для a(i)(j) то, что входит / выходит за пределы индекса, j не зависит от i . (Это то, что представляет собой 2D-массив, например, в C .)

Ответ №2:

Вам просто нужно проверить массив по индексу i с isDefinedAt , существует ли он:

 def do_to_elt(i:Int, j:Int): Unit =
  if (a.isDefinedAt(i) amp;amp; a(i).isDefinedAt(j)) f(a(i)(j))
  

РЕДАКТИРОВАТЬ: Пропустил ту часть об элегантном решении, поскольку я сосредоточился на ошибке в коде перед вашей правкой.

Что касается элегантности: нет, по сути, нет способа выразить это более элегантным способом. Некоторые могут посоветовать вам использовать шаблон pimp-my-library, чтобы он выглядел более элегантно, но на самом деле в данном случае это не так.

Если ваш единственный вариант использования — выполнить функцию с элементом многомерного массива, когда индексы допустимы, то этот код делает это, и вы должны его использовать. Вы могли бы обобщить метод, изменив сигнатуру, чтобы использовать функцию для применения к элементу и, возможно, значение, если индексы недопустимы, как это:

 def do_to_elt[A](i: Int, j: Int)(f: Int => A, g: => A = ()) = 
  if (a.isDefinedAt(i) amp;amp; a(i).isDefinedAt(j)) f(a(i)(j)) else g
  

но я бы не стал ничего менять помимо этого. Это также не выглядит более элегантно, но расширяет ваш вариант использования.

(Также: если вы работаете с массивами, вы в основном делаете это из соображений производительности, и в этом случае может быть даже лучше не использовать isDefinedAt , а выполнять проверки достоверности на основе длины массивов.)

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

1. Да, это то, что я сделал. Я надеялся на что-то более элегантное.