Scala core Flashcards
Q.29. Explain sealed classes in Scala.
Когда мы помечаем trait или класс как sealed, мы должны объявить все подтипы этого класса или трейта в этом же файле. Таким образом, мы можем убедиться, что инициализации подтипа родительского типа не будут раскиданны по всему проекту - мы их все видеть в одном файле.
*****
When we mark a trait or a class as sealed, we must declare all subtypes in the same file. This way, we can make sure that we know all subtypes. ----------- scala\> sealed abstract class Transport defined class Transport scala\> case class Scooter() extends Transport defined class Scooter scala\> case class Car() extends Transport defined class Car scala\> def show(vehicle:Transport):String=vehicle match{ | case a: Scooter=\>"Two wheels" | case b: Car=\>"Four wheels" | }
- What is Unit?
Unit - это тип, который представляет отсутствие значения, как Java void. Это подтип scala.AnyVal. Существует только одно значение типа Unit, представленное (), и оно не представлено ни одним объектом , а служит определенным маркером для обозначения участков кода которые являются процедурами а не выражениями ( expression ) результат действия, которых будет side effect.
*****
Unit is a type which represents the absence of value, just like Java void. It is a subtype of scala.AnyVal. There is only one value of type Unit, represented by (), and it is not represented by any object in the underlying runtime system.
Q.13. What is a Guard in a for-comprehension?
Когда in-for comprehension мы хотим фильтровать элементы, мы можем использовать условие if. Мы можем назвать это условие if «охранником». Когда охранник имеет значение True, Scala добавляет этот элемент в новую коллекцию. В противном случае он не выполняет никаких действий. Давайте возьмем пример.
val nums = список (0,1,2,3,4,5,6,7,8,9,10)
для {num <-nums если num% 3 == 0
} yield num
res0: List [Int] = List (0, 3, 6, 9)
Этот код объявляет список и добавляет в другой список только те элементы, которые делятся на 3.
- What is a case class?
case class является синтаксическим сахаром для класса, объекты которого является неизменным и разложимым посредством patten maching (потому что у них есть методы apply и unapply). Быть разложимым означает, что можно извлекать параметры его конструкторов при сопоставлении с образцом ( при приминение к нему конструкции pattern matching ) .
Case Классы содержат сопутствующий объект, который содержит метод apply, который создается по умолчанию без написания дополнительного кода. Этот факт делает возможным создание экземпляра класса case без ключевого слова new. Объекты Case classes также по дефолту поставляются с некоторыми вспомогательными методами, такими как метод .copy, который облегчает создание слегка измененной копии с оригинала, метод HashCode , equal , toString, все возможноные конструкторы и тд
Наконец, case классы сравниваются по значение полей объекта вместо сравнения по ссылке, т. Е. Они идут с методом, который сравнивает два объекта кейс класса по их значениям / полям вместо сравнения только ссылок ( если все поля 2х объектов кейс классов(одинакого типа ) совпадают то оператор == вернет true . В java для активации такого функционала надо было explicitly ( явно ) override ( переопределять ) метод equal специальным образом, т.е. писать много boilerplate code ( стандартного кода )
Классы Case особенно полезны для использования в качестве DTO - data transfer object pattern. Так же case classes активно используются в akka actor И при работе с json.
*****
A case class is syntactic sugar for a class that is immutable and decomposable through pattern matching (because they have an apply and unapply methods). Being decomposable means it is possible to extract its constructors parameters in the pattern matching.
Case classes contain a companion object which holds the apply method. This fact makes possible to instantiate a case class without the new keyword. They also come with some helper methods like the .copy method, that eases the creation of a slightly changed copy from the original.
Finally, case classes are compared by structural equality instead of being compared by reference, i.e., they come with a method which compares two case classes by their values/fields, instead of comparing just the references.
Case classes are specially useful to be used as DTOs.
- What is function currying?
def add (a: Int) (b: Int) = a + b
val add2 = add (2) (_)
scala> add2 (3)
res0: Int = 5
—————
Карри полезно во многих различных контекстах, но чаще всего, когда вам приходится иметь дело с функциями высшего порядка.
*****
Currying is a technique of making a function that takes multiple arguments into a series of functions that take a part of the arguments. --------------- def add(a: Int)(b: Int) = a + b
val add2 = add(2)(_)
scala> add2(3)
res0: Int = 5
—————
Currying is useful in many different contexts, but most often when you have to deal with Higher order functions.
- What is the difference between a call-by-value and call-by-name parameter?
The difference between a call-by-value and a call-by-name parameter, is that the former is computed before calling the function, and the later is evaluated when accessed.
As it may be seen, the call-by-name example makes the computation only when needed, and every time it is called, while the call-by-value only computes once, but it computes before invoking the function (callByName).
- What is the difference between a var, a val and def and lazy val ?? and method vs function ??
Методы это выражения связанные с объектом класса. Таким образом, мы можем вызвать его в рамках экземпляра класса, но не можем вызвать его напрямую без объекта в отличие от функции. Мы можем определить метод только в ассоциации с классом или с трейдом.
Функция, однако, не ассоциируется с классом или трейтом. Мы можем получить к ней доступ без объекта ( экземпляра класса ) , и мы можем определить ее в пакете Scala без какой либо ассоциации с трейтом или с классом.
Var - это переменная. Это изменчивая ссылка на значение. Поскольку она изменчива, ее значение может изменяться в течение времени жизни программы. Имейте в виду, что тип переменной не может изменяться в Scala. Вы можете сказать, что var ведет себя подобно переменным в Java.
Val - это константа. Это неизменная ссылка, означающая, что значенние этой константы никогда не меняется. После назначения val всегда будет иметь одинаковое значение. Это похоже на константы в других языках. В java это похоже на инициализацию переменной с ключ словом final
https://stackoverflow.com/questions/25417450/how-is-val-in-scala-different-from-const-in-java
Def создает метод (а метод отличается от функции см выше ). Оценивается по вызову.
Бонус: что такое lazy val ? Это почти как val, но его значение вычисляется только при необходимости ( как def ) и только один раз ( как val ) в отличие от def. Это особенно полезно, чтобы избежать тяжелых вычислений .
https://pedrorijo.com/blog/scala-interview-questions/#1-what-is-the-difference-between-a-var-a-val-and-def
??? Там, где тщательная оценка выполняется во время компиляции, ленивая оценка выполняется во время выполнения. \ ???
****
Methods associated with an object. So we can call it on an instance of a class, but cannot call it directly without an object. We can define it in a class or in a trait. A function, however, doesn’t associate with a class or with a trait. We can access it without an object, and we define it in a Scala package.
A var is a variable. It’s a mutable reference to a value. Since it’s mutable, its value may change through the program lifetime. Keep in mind that the variable type cannot change in Scala. You may say that a var behaves similarly to Java variables.
A val is a value. It’s an immutable reference, meaning that its value never changes. Once assigned it will always keep the same value. It’s similar to constants in another languages.
A def creates a method (and a method is different from a function - thanks to AP for his comment). It is evaluated on call.
Bonus: what’s a lazy val? It’s almost like a val, but its value is only computed when needed. It’s specially useful to avoid heavy computations (using short-circuit for instance).
??? Where eager evaluation is at compile-time, lazy evaluation is at run-time.\???
Q.30. Can you reassign parameters for case classes?
Нет, мы не можем, поскольку case классы помогают моделировать неизменяемые данные. Его параметры неявно public val . Мы могли бы использовать var здесь, но Скала не рекомендует делать это. Если уж больно надо мы можем использовать метод .copy создать новый кейс объект с изменнеными полями от искомого кейс объекта.
*****
No, we cannot, since case classes help model immutable data. Its parameters are implicitly public val. We could use vars here, but Scala discourages doing so.
- What is the difference between the following terms and types in Scala: Nil, Null, None, Nothing?
None - это пустое представление монады Option - решение проблемы null pointer exception
Null - это trait Scala, где null - единственный его экземпляр. The null value пришгло из Java и является экземпляром любого объекта, то есть это подтип всех ссылочных ( AnyRef ) типов ( см иерархию типов в скале ), но не типов значений ( AnyVal ) . Он существует для того, чтобы ссылочные типы могли иметь нулевое значение, а типы значений (например, Int или Long) не могут обращаться в null ( но это не точно -перепроверить ??? ) .
Nothing является другим трейтом Скалы. Это подтип любого другого типа ( см иерархию ) , и у него нет подтипов. Он существует благодаря сложной системе типов, которую имеет Scala. У него ноль экземпляров. Это тип возврата метода, который никогда не возвращается нормально, например, метод, который всегда выдает исключение ( эксепшен ) . Причина, по которой Scala имеет тип bottom, связана с его способностью выражать ( чо бля ??? ) дисперсию в параметрах типа. Nothing удобно использовать в левой части монады Either - для обозначения что левая часть выкидывает какие либо исключения или ошибки.
Наконец, Nil представляет пустой List, т.е. List c нулевым размером. Nil имеет тип List [Nothing].
Все эти типы могут создать чувство пустоты, верно? Вот небольшая помощь в понимании пустоты в Scala.
****
The None is the empty representation of the Option monad (see answer #11).
Null is a Scala trait, where null is its only instance. The null value comes from Java and it’s an instance of any object, i.e., it is a subtype of all reference types, but not of value types. It exists so that reference types can be assigned null and value types (like Int or Long) can’t.
Nothing is another Scala trait. It’s a subtype of any other type, and it has no subtypes. It exists due to the complex type system Scala has. It has zero instances. It’s the return type of a method that never returns normally, for instance, a method that always throws an exception. The reason Scala has a bottom type is tied to its ability to express variance in type parameters..
Finally, Nil represents an empty List of anything of size zero. Nil is of type List[Nothing].
All these types can create a sense of emptiness right? Here’s a little help understanding emptiness in Scala.
- What are High Order Functions?
Функции высокого порядка - это функции, которые могут получать или возвращать другие функции. Типичными примерами в Scala являются функции .filter, .map, .flatMap, которые получают другие функции в качестве аргументов.
*****
High order functions are functions that can receive or return other functions. Common examples in Scala are the .filter, .map, .flatMap functions, which receive other functions as arguments.
Q.21. It is impossible to mark constructor parameters as private in Scala?
Нет, это не так. Мы можем объявить параметр(поле) класса как закрытый, если объявим его без ключевого слова var или val. Тогда мы можем получить доступ к значениям его полей(параметров) только внутри класса.
- How does yield work? What operations is a for comprehension syntactic sugar for?
scala> for (i <- 1 to 5) yield i * 2
res0: scala.collection.immutable.IndexedSeq [Int] = Vector (2, 4, 6, 8, 10)
А для понимания - это альтернативный синтаксис для составления нескольких операций над монадами.
Для понимания можно заменить операции for compehension => map / flatMap и filter ( withFilter).
for {
х <- с1
у <- с2
z <- c3, если z> 0
} yield {…}
c1.flatMap (x => c2.flatMap (y => c3.withFilter (z => z> 0) .map (z => {…})))
***
scala> for (i <- 1 to 5) yield i * 2
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
A for comprehension is a alternative syntax for the composition of several operations on monads.
A for comprehension can be replaced by foreach operations (if no yield keyword is being used), or by map/flatMap and filter (actually, while confirming my words I found out about the withFilter method).
for {
x <- c1
y <- c2
z <- c3 if z > 0
} yield {…}
c1.flatMap(x => c2.flatMap(y => c3.withFilter(z => z > 0).map(z => {…})))
****
Q.26. What is a closure in Scala?
объект Demo {
def main (args: Array [String]) {
println (“multiplier (1) значение =” + multiplier (1))
println (“multiplier (2) значение =” + multiplier (2))
}
var factor = 3
val multiplier = (i: Int) => i * factor
}
————-
multiplier - will be a closure
- What is Tail recursion?
В «нормальных» рекурсивных методах, метод вызывает сам себя внутри самого себя в определенном этапе хода выполнение кода. Этот метод используется, например, в наивной реализации числа Фибоначчи. Проблема с этим подходом состоит в том, что на каждом рекурсивном шаге в memory стеке необходимо сохранять другую порцию информации ( промежуточное вычисление предыдущих шагов этой рекурсии ). Если шагов рекурсии очень много ( больше 50ти ), как правило это приводит к runtime exception stackoverflow.
Хвостовая рекурсия решает эту проблему. Вы можете попросить компилятор принудительно использовать хвостовую рекурсию в методе с помощью аннотации @tailrec. Хвостовые рекурсивные методы реализованны разработчиком таким образом что все вычисления выполняются перед рекурсивным вызовом, а последним оператором является рекурсивный вызов. Если пометить метод аннотацией @tailrec и написать код по другому ( не правильно ), то будет ошибка компеляции. Затем компиляторы могут воспользоваться этим свойством, чтобы избежать ошибок переполнения стека, поскольку хвостовые рекурсивные вызовы можно оптимизировать, не вставляя промежуточные шаги рекурсивного вычисления в memory стек.
*****
In “normal” recursive methods, a method calls itself at some point. This technique is used in the naive implementation of the Fibonacci number, for instance. The problem with this approach is that at each recursive step, another chunk of information needs to be saved on the stack. In some cases, an huge number of recursive steps can occur, leading to stack overflow errors.
Tail recursion solves this problem. In tail recursive methods, all the computations are done before the recursive call, and the last statement is the recursive call. Compilers can then take advantage of this property to avoid stack overflow errors, since tail recursive calls can be optimized by not inserting info into the stack.
You can ask the compiler to enforce tail recursion in a method with @tailrec
heirarchy of classes in scala ? Q.6. What is the equivalent type for java.lang.Object here in Scala?
Where there’s java.lang.Object in Java, we have AnyRef in Scala. This is in context of a Java Runtime Environment (JRE).
https://data-flair.training/blogs/scala-interview-questions-with-answers/