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

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

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

в scala?

Ответ 1

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

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

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

Ответ 2

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

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

EDIT: пропустил эту часть об элегантном решении, поскольку я сосредоточился на ошибке в коде перед редактированием.

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

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

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

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

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