Может ли Scala звонить по ссылке?
Я знаю, что Scala поддерживает вызов по имени из ALGOL, и я думаю, что понимаю, что это значит, но может ли Scala выполнять вызов по ссылке, как C#, VB.NET и C++? Я знаю, что Java не может выполнять вызов по ссылке, но я не уверен, связано ли это ограничение исключительно с языком или также с JVM.
Это было бы полезно, когда вы хотите передать огромную структуру данных методу, но не хотите делать его копию. Вызов по ссылке кажется идеальным в этом случае.
3 ответа
Java и Scala используют исключительно вызов по значению, за исключением того, что значение является либо примитивом, либо указателем на объект. Если ваш объект содержит изменяемые поля, то между этим и вызовом по ссылке очень мало существенных различий.
Так как вы всегда передаете указатели на объекты, а не на сами объекты, у вас нет проблемы повторного копирования гигантского объекта.
Кстати, вызов Scala по имени реализован с использованием вызова по значению, причем значением является (указатель на) функциональный объект, который возвращает результат выражения.
Для языка, где "все является объектом" и ссылка на объект недоступна, например, Java и Scala, каждый параметр функции является ссылкой, передаваемой по значению на некотором уровне абстракции ниже языка. Однако, с точки зрения семантики языковой абстракции, существует либо вызов по ссылке, либо вызов по значению, в зависимости от того, предоставляется ли функции копия ссылочного объекта. В этом случае термин "вызов по совместному использованию" охватывает как вызов по ссылке, так и вызов по значению на уровне абстракции языка. Таким образом, было бы правильно сказать, что Java является вызовом по значению на уровне абстракции ниже семантики языка (то есть по сравнению с тем, как она будет гипотетически переводиться в C или в байт-код для виртуальной машины), в то же время говоря, что Java и Scala - это (за исключением встроенных типов) вызов по ссылке в семантике своей абстракции "все является объектом".
В Java и Scala некоторые встроенные типы (a/k/a примитив) автоматически передаются по значению (например, int или Int), и каждый определенный пользователем тип передается по ссылке (то есть необходимо вручную скопировать их, чтобы передать только их ценность).
Обратите внимание, что я обновил раздел Википедии " Call-by-share", чтобы сделать это более понятным.
Возможно, Википедия не понимает различий между передачей по значению и вызовом по значению? Я думал, что передача по значению является более общим термином, так как он применяется к выражениям присваивания, а также к применению функции. Я не удосужился попытаться сделать это исправление в Википедии, оставь это для других, чтобы они их хешировали.
Нет разницы на уровне семантики, где "все является объектом" между вызовом по ссылке и вызовом по значению, когда объект неизменен. Таким образом, язык, который допускает объявление вызова по значению по сравнению с вызовом по ссылке (такой как язык, подобный Scala, который я разрабатываю), может быть оптимизирован путем задержки копирования по значению, пока объект не будет изменен.
Люди, которые проголосовали за это, очевидно, не понимают, что такое "обмен вызовами".
Ниже я добавлю описание, которое я написал для моего языка Copute (который ориентирован на JVM), где я обсуждаю стратегию оценки.
Даже с чистотой, полный язык Тьюринга (т.е. допускающий рекурсию) не является полностью декларативным, потому что он должен выбирать стратегию оценки. Стратегия оценки - это относительный порядок оценки времени выполнения между функциями и их аргументами. Стратегия оценки функций может быть строгой или нестрогой, что аналогично стремлению или ленивости соответственно, поскольку все выражения являются функциями. Eager означает, что выражения аргумента вычисляются до их функции; тогда как ленивый означает, что выражения аргумента вычисляются (один раз) в момент времени их первого использования в функции. Стратегия оценки определяет компромисс между производительностью, детерминизмом, отладкой и операционной семантикой. Для чистых программ это не изменяет результат денотационной семантики, потому что с чистотой императивные побочные эффекты порядка оценки вызывают только неопределенность в (то есть категорически ограничены) потреблении памяти, времени выполнения, времени ожидания и незавершенных доменах.,
По сути, все выражения являются (композицией) функций, то есть константы являются чистыми функциями без входов, унарные операторы являются чистыми функциями с одним входом, бинарные операторы являются чистыми функциями с двумя входами, конструкторы являются функциями и даже управляющими операторами (например, если, для, while) можно смоделировать с помощью функций. Порядок, в котором мы оцениваем эти функции, не определяется синтаксисом, например, f( g()) может охотно оценивать g, затем f по результату g, или он может оценивать f и только лениво оценивать g, когда его результат необходим в пределах f.
Первый (нетерпеливый) - это вызов по значению (CBV), а второй (ленивый) - это вызов по имени (CBN). CBV имеет вариант call-by-share, распространенный в современных языках ООП, таких как Java, Python, Ruby и т. Д., Где нечистые функции неявно вводят некоторые изменяемые объекты посредством ссылки. CBN имеет вариант вызова по требованию (также CBN), где аргументы функции оцениваются только один раз (что не совпадает с функциями запоминания). Call-by-потребность почти всегда используется вместо call-by-name, потому что это экспоненциально быстрее. Обычно оба варианта CBN появляются только с чистотой из-за диссонанса между объявленной иерархией функций и порядком оценки времени выполнения.
Языки обычно имеют стратегию оценки по умолчанию, а некоторые имеют синтаксис для необязательного принудительного вычисления функции в нестандартном режиме. Языки, которые стремятся по умолчанию, обычно лениво оценивают операторы логического конъюнкции (a/k/a "и", &&) и дизъюнкции (a/k/a "или", ||), потому что второй операнд не нужен в половине случаев, т.е. верно || все == правда и ложь && что угодно == ложь.
Вот как эмулировать эталонные параметры в Scala.
def someFunc( var_par_x : Function[Int,Unit] ) {
var_par_x( 42 ) // set the reference parameter to 42
}
var y = 0
someFunc( (x => y=x) )
println(y)
Ну, ладно, не совсем то, к чему привыкли программисты на Pascal или C++; но тогда очень мало в скале есть. Положительным моментом является то, что это дает вызывающей стороне больше гибкости с тем, что они могут делать со значением, отправленным параметру. Например
someFunc( (x => println(x) ) )
Я читал по этой теме и видел эти вопросы и ответы. Я стал с этим примером, который действительно работает, если вы скопируете и вставите его в текстовый файл и сохраните его как *.bat, например testref.bat:)
::#!
@echo off
call scala -savecompiled %0 %*
goto :eof
::!#
class X( var x: Array[String] )
val mybb = new X(Array("ss", "dd"))
println("Printing x array:")
mybb.x.foreach( e => println(e))
mybb.x = mybb.x :+ "Carlos"
println("Printing x array, carlos added:")
mybb.x.foreach( e => println(e))
updateMyVar(mybb)
println("Printing x array, kassab added:")
mybb.x.foreach( e => println(e))
def updateMyVar( mycc: X ): Unit =
{
mycc.x = mycc.x :+ "Kassab"
}
Я просто хотел бы подтвердить, действительно ли это способ вызова переменных по ссылке?