Оператор Increment (++) в Scala
Есть ли какая-либо причина для Scala не поддерживать оператор ++ для приращения примитивных типов по умолчанию?
Например, вы не можете писать:
var i=0
i++
Спасибо
Ответы
Ответ 1
Я предполагаю, что это было опущено, потому что оно будет работать только для изменяемых переменных, и это не имеет смысла для неизменных значений. Возможно, было решено, что оператор ++
не критикует назначение, поэтому включение его может привести к ошибкам в отношении того, изменяете ли вы переменную.
Я чувствую, что что-то вроде этого безопасно делать (на одной строке):
i++
но это будет плохой практикой (на любом языке):
var x = i++
Вы не хотите смешивать утверждения контировки и побочные эффекты/мутацию.
Ответ 2
Мне нравится Craig ответить, но я думаю, что этот вопрос должен быть более решительным.
-
Нет никаких "примитивов" - если Int
может это сделать, тогда может быть пользовательский Complex
(например).
-
Основное использование ++
будет таким:
var x = 1 // or Complex(1, 0)
x++
-
Как вы реализуете ++
в классе Complex
? Предполагая, что, как и Int
, объект неизменен, тогда метод ++
должен возвращать новый объект, но этот новый объект должен быть назначен.
Для этого потребуется новая языковая функция. Например, предположим, что мы создаем ключевое слово assign
. Также необходимо изменить сигнатуру типа, чтобы указать, что ++
не возвращает Complex
, но назначает его любому полю, содержащему данный объект. В Scala духе не вторгаясь в пространство имен программистов, скажем, мы делаем это, префикс типа @
.
Тогда это может быть так:
case class Complex(real: Double = 0, imaginary: Double = 0) {
def ++: @Complex = {
assign copy(real = real + 1)
// instead of return copy(real = real + 1)
}
Следующая проблема заключается в том, что операторы постфикса сосать с правилами Scala. Например:
def inc(x: Int) = {
x++
x
}
Из-за правил Scala это то же самое, что:
def inc(x: Int) = { x ++ x }
Это не было целью. Теперь Scala имеет привилегированный стиль: obj method param method param method param ...
. Это хорошо сочетает традиционный синтаксис С++/Java object method parameter
с концепцией функционального программирования конвейеризации ввода через несколько функций для получения конечного результата. Этот стиль недавно был назван "беглыми интерфейсами".
Проблема заключается в том, что, привилегируя этот стиль, он калечит постфиксные операторы (и префиксные, но Scala едва ли их вообще). Итак, в конце концов, Scala должен был бы внести большие изменения, и в любом случае он сможет измерить элегантность операторов приращения и уменьшения числа C/Java, если только он действительно не уйдет от того, что он поддерживает.
Ответ 3
В Scala, ++ является допустимым методом, и никакой метод не подразумевает назначение. Только =
может это сделать.
Более длинный ответ заключается в том, что языки, такие как С++ и Java, относятся к ++
специально, а Scala относится к =
специально и непоследовательно.
В Scala при написании i += 1
компилятор сначала ищет метод под названием +=
в Int. Это не так, так что теперь это волшебство на =
и пытается скомпилировать строку так, как будто она читает i = i + 1
. Если вы напишете i++
, то Scala вызовет метод ++
на i
и присвойте результат... ничего. Потому что только =
означает назначение. Вы можете написать i ++= 1
, но этот вид поражения цели.
Тот факт, что Scala поддерживает имена методов, такие как +=
, уже вызывает противоречие, и некоторые люди считают его перегрузкой оператора. Они могли бы добавить специальное поведение для ++
, но тогда оно больше не будет корректным именем метода (например, =
), и это будет еще одна вещь, о которой нужно помнить.
Ответ 4
Я думаю, что рассуждение отчасти состоит в том, что +=1
- это еще один символ, а ++
используется довольно сильно в коде коллекций для конкатенации. Поэтому он сохраняет очиститель кода.
Кроме того, Scala поддерживает неизменяемые переменные, а ++
- внутренняя операция мутации. Если вам требуется +=
, по крайней мере, вы можете заставить все свои мутации пройти общую процедуру назначения (например, def a_=
).
Ответ 5
Конечно, вы можете иметь это в Scala, если вы действительно хотите:
import scalaz._
import Scalaz._
case class IncLens[S,N](lens: Lens[S,N], num : Numeric[N]) {
def ++ = lens.mods(num.plus(_, num.one))
}
implicit def incLens[S,N:Numeric](lens: Lens[S,N]) =
IncLens[S,N](lens, implicitly[Numeric[N]])
val i = Lens[Int,Int](identity, (x, y) => y)
val imperativeProgram = for {
_ <- i := 0;
_ <- i++;
_ <- i++;
x <- i++
} yield x
def runProgram = imperativeProgram ! 0
И вот вы идете:
scala> runProgram
runProgram: Int = 3
Ответ 6
Основная причина в том, что в Scala нет необходимости, как в C. В C вы постоянно:
for(i = 0, i < 10; i++)
{
//Do stuff
}
В С++ добавлены методы более высокого уровня, чтобы избежать явных циклов, но Scala значительно продвинулся вперед, обеспечивая foreach, map, flatMap foldLeft и т.д. Даже если вы действительно хотите работать с последовательностью целых чисел, а не просто ездить на велосипеде, хотя набор нецелых объектов, вы можете использовать диапазон Scala.
(1 to 5) map (_ * 3) //Vector(3, 6, 9, 12, 15)
(1 to 10 by 3) map (_ + 5)//Vector(6, 9, 12, 15)
Поскольку оператор ++ используется библиотекой коллекций, я чувствую, что лучше избегать его использования в классах не коллекции. Раньше я использовал ++ как метод возврата значения в мой пакетный пакет Util так:
implicit class RichInt2(n: Int)
{
def isOdd: Boolean = if (n % 2 == 1) true else false
def isEven: Boolean = if (n % 2 == 0) true else false
def ++ : Int = n + 1
def -- : Int = n - 1
}
Но я удалил его. В большинстве случаев, когда я использовал ++ или + 1 для целого числа, я позже нашел лучший способ, который этого не требует.
Ответ 7
Возможно, если вы определите собственный класс, который может имитировать желаемый результат, но может быть больно, если вы хотите использовать обычные методы "Int", так как вам всегда нужно будет использовать *()
import scala.language.postfixOps //otherwise it will throw warning when trying to do num++
/*
* my custom int class which can do ++ and --
*/
class int(value: Int) {
var mValue = value
//Post-increment
def ++(): int = {
val toReturn = new int(mValue)
mValue += 1
return toReturn
}
//Post-decrement
def --(): int = {
val toReturn = new int(mValue)
mValue -= 1
return toReturn
}
//a readable toString
override def toString(): String = {
return mValue.toString
}
}
//Pre-increment
def ++(n: int): int = {
n.mValue += 1
return n;
}
//Pre-decrement
def --(n: int): int = {
n.mValue -= 1
return n;
}
//Something to get normal Int
def *(n: int): Int = {
return n.mValue
}
Некоторые возможные тестовые примеры
scala>var num = new int(4)
num: int = 4
scala>num++
res0: int = 4
scala>num
res1: int = 5 // it works although scala always makes new resources
scala>++(num) //parentheses are required
res2: int = 6
scala>num
res3: int = 6
scala>++(num)++ //complex function
res4: int = 7
scala>num
res5: int = 8
scala>*(num) + *(num) //testing operator_*
res6: Int = 16
Ответ 8
Позволяет определить var:
var i = 0
++ я уже достаточно короткий:
{i+=1;i}
Теперь я ++ может выглядеть так:
i(i+=1)
Чтобы использовать синтаксис выше, определите где-нибудь внутри объекта пакета, а затем импортируйте:
class IntPostOp(val i: Int) { def apply(op: Unit) = { op; i } }
implicit def int2IntPostOp(i: Int): IntPostOp = new IntPostOp(i)
Возможна также цепочка операторов:
i(i+=1)(i%=array.size)(i&=3)
Приведенный выше пример похож на этот код Java (С++?):
i=(i=i++ %array.length)&3;
Конечно, стиль может зависеть.
Ответ 9
Он не включен, потому что разработчики Scala считали, что он делает сложную спецификацию при достижении лишь незначительных преимуществ и потому что Scala вообще не имеет операторов.
Вы можете написать свой собственный вот так:
class PlusPlusInt(i: Int){
def ++ = i+1
}
implicit def int2PlusPlusInt(i: Int) = new PlusPlusInt(i)
val a = 5++
// a is 6
Но я уверен, что у вас возникнут проблемы с приоритетом, который не работает, как вы ожидаете. Кроме того, если я ++ будет добавлен, люди тоже будут запрашивать ++ i, что действительно не вписывается в синтаксис Scala.