Задачи на JPoint 2018. Luxoft. День 2

Задача 1

public class Quiz_1 {

    public static void print() {
        System.out.print("A");
    }
    
    public static void main(String[] args) {
        ((Quiz_1) null).print();
        System.out.print("B");
    }
    
}

Варианты ответов:

  1. AB
  2. B
  3. NullPointerException
  4. Compilation error
Правильный ответ

1. AB

Из документации 15.12.4.1. Compute Target Reference (If Necessary) instanceof можно узнать, что вызов статического метода не требует вычисления ссылки на инстанс, поэтому никакой ошибки не произойдёт.


Задача 2

public class Quiz_2 {

    public void func(Integer i) {
        System.out.println("integer"); }
    public void func(Double i) {
        System.out.println("double"); }
    public void func(Object i) {
        System.out.println("object"); }

    public static void main(String[] args) {
        List nums = Arrays.asList(1/2);
        new Quiz_2().func(nums.get(0));
    }
}

Варианты ответов:

  1. integer
  2. double
  3. object
  4. Runtime exception
  5. Compilation error
Правильный ответ

3. object

… из-за того, что список List<?> nums типизирован <?>. Такой вайлдкард вернёт Object в любой ситуации, а компилятор заранее скомпилирует представленный код с вызовом правильной функции, а именно public void func(Object i) {.


Задача 3

public class Quiz_3 {

    public static void main(String[] args) {
        System.out.println(
                true?false:true == true?false:true
        );
    }
}

Варианты ответов:

  1. true
  2. false
  3. Compilation error
Правильный ответ

2. false

Тернарный оператор вычисляется слева направо.


Задача 4

public class Quiz_4 {

    public static void main(String[] args) {
        int n = 1;
        int result = Optional.of(n++)
                .map(i -> i + n)
                .orElse(-1);
        System.out.println(result);
    }
}

Варианты ответов:

  1. 2
  2. 3
  3. 4
  4. -1
  5. Runtime exception
  6. Compilaction error
Правильный ответ

6. Compilation error

Переменная n должна быть финальной, чтобы использоваться в лямбда-выражении i -> i + n. С другой стороны, n не может быть финальной из-за унарного оператора ++ в выражении Optional.of(n++).


Задача 5

public class Quiz_5 {

    public static void main(String[] args) {
        int n = Integer.MAX_VALUE;
        n++;
        System.out.println(n + n);
    }
}

Варианты ответов:

  1. -2
  2. 0
  3. 2
  4. 4294967296 (2^32)
  5. Runtime exception
  6. Compilation error
Правильный ответ

2. 0

Начнём с того, что Integer.MAX_VALUE + 1 = Integer.MIN_VALUE. В бинарном представлении значение Integer.MAX_VALUE (01111111111111111111111111111111) при добавлении к нему единицы получится Integer.MIN_VALUE (10000000000000000000000000000000). При сложении 2-х этих чисел сумма старших битов даст 102, что приведёт к переполнению в памяти. Поскольку 1 будет некуда записать в 32-х битном представлении, то останутся только нули.

Задачи на JPoint 2018. Luxoft. День 1

Задача 1

public class Quiz_1 {

    public static void main(String[] args) {
        String s = null;
        System.out.println(s instanceof String);
    }
    
}

Варианты ответов:

  1. true
  2. false
  3. NullPointerException
  4. Compilaction error
Правильный ответ

2. false

Согласно пункту документации 15.20.2. Type Comparison Operator instanceof:

At run time, the result of the instanceof operator is true if the value of the RelationalExpression is not null and the reference could be cast (§15.16) to the ReferenceType without raising a ClassCastException.


Задача 2

public class Quiz_2 {

    public static void main(String[] args) {
        String[] strings = {"1", "1"};
        Object[] objects = strings;
        objects[0] = 0;
        System.out.println(
                Arrays.toString(objects)
        );
    }
    
}

Варианты ответов:

  1. [0, 2]
  2. [1, 2]
  3. Runtime exception
  4. Compilation error
Правильный ответ

3. Runtime exception

Аналогичный пример даже указан в javadoc для класса java.lang.ArrayStoreException


Задача 3

public class Quiz_3 {

    public static void main(String[] args) {
        String str = "true";
        if ("t" + "r" + "u" + "e" == "true")
            System.out.print("1");
        if (str + "" == "true")
            System.out.print("2");
        if (str.replace('T', 't') == "true")
            System.out.print("3");
    }
    
}

Варианты ответов:

  1. 123
  2. 12
  3. 23
  4. 13
  5. 1
  6. 2
  7. 3
Правильный ответ

4. 13

  1. Выражение "t" + "r" + "u" + "e" является строковым литералом и согласно спецификации 3.10.5. String Literals:
    A long string literal can always be broken up into shorter pieces and written as a (possibly parenthesized) expression using the string concatenation operator +
    [...]
    Moreover, a string literal always refers to the same instance of class String.
    
        Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals.
        Strings computed by concatenation at run-time are newly created and therefore distinct.
    

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

       L0
        LINENUMBER 15 L0
        LDC "true"
        ASTORE 1
       L1
        LINENUMBER 17 L1
        GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
        LDC "1"
        INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
    

    L0 соответствует сохарнению значения «true» в переменную, а L1 соответствует выводу «1» через System.out, любые проверки отсутствуют.

  2. Компилятор вместо оператора конкатенации «+» в байт-коде использует java.lang.StringBuilder:
       L2
        LINENUMBER 18 L2
        NEW java/lang/StringBuilder
        DUP
        INVOKESPECIAL java/lang/StringBuilder. ()V
        ALOAD 1
        INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
        LDC ""
        INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
        INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
        LDC "true"
        IF_ACMPNE L3
    

    Метод StringBuilder.toString() возвращает новый экземпляр типа String с новой ссылкой:

    @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }
    

    Именно поэтому сравнение через == вернёт значение false.

  3. В соответствии с javadoc метода replace():
    If the character {@code oldChar} does not occur in the character sequence represented by this {@code String} object, then a reference to this {@code String} object is returned.
    

    Поскольку 'T' отсутствует в исходной строке, то вернётся исходный объект.


Задача 4

public class Quiz_4 {

    public static void main(String[] args) {
        Stream stream =
                Stream.of("A", "B");
        System.out.print(1);
        stream.peek(System.out::print);
        System.out.print(2);
        stream.forEach(System.out::print);
    }
    
}

Варианты ответов:

  1. 1AB2AB
  2. 1A2A1B2B
  3. 12AB
  4. 12ABAB
  5. Runtime exception
  6. Compilation error
Правильный ответ

5. Runtime exception

А конкретно:

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at me.markoutte.sandbox.conferences.jpoint2018.luxoft.day1.Quiz_4.main(Quiz_4.java:21)

Происходит это всё из-за того, что peek является промежуточной операцией, возвращающей новый стрим, который в данном примере отсутствует, а вызов forEach обнаруживает проблему чтения из того же стрима, что читает peek.


Задача 5

public class Quiz_5 {

    public static void main(String[] args) {
        HashSet set = new HashSet<>();
        for (short i = 0; i < 100; i++) {
            set.add(i);
            set.remove(i - 1);
        }
        System.out.println(set.size());
    }
    
}

Варианты ответов:

  1. 0
  2. 1
  3. 100
  4. Runtime exception
  5. Compilation error
Правильный ответ

3. 100

Ни одно из значений не будет удалено из сета, поскольку для операции (i - 1) будет автоматически выполнено сначала расширения типа до integer, а потом боксинг в тип Integer. Поскольку в сете хранится тип Short, а не Integer, то ни одна из операций по удалению не сможет найти нужных значений и величина массива будет равна количеству операции по добавлению. В данном случае таковых 100.

Коварный дабл

Вы и так это знали:

Тип double не очень точный тип данных

Решите, например, сегодняшнее задание на codewars:

Your task is to construct a building which will be a pile of n cubes. The cube at the bottom will have a volume of n^3, the cube above will have volume of (n-1)^3 and so on until the top which will have a volume of 1^3.

You are given the total volume m of the building. Being given m can you find the number n of cubes you will have to build?

The parameter of the function findNb (find_nb, find-nb, findNb) will be an integer m and you have to return the integer n such as n^3 + (n-1)^3 + … + 1^3 = m if such a n exists or -1 if there is no such n.

Examples:

findNb(1071225) --> 45
findNb(91716553919377) --> -1

https://www.codewars.com/kata/5592e3bd57b64d00f3000047

Переведу в кратце: необходимо вычислить количество кубиков для здания заданного объёма. Объём здания считается как сумма объёмов состовляющих его кубиков. Каждый кубик, начиная с нижнего, имеет объём (n - i + 1)^3, где i — порядковый номер куба (от 1 до n) снизу.

Решение в лоб с использованием циклов:

public static long findNb(long m) {
    long n = 0, totalVolume = 0;
    while (totalVolume < m) {
        totalVolume += ++n * n * n;
    }
    return totalVolume == m ? n : -1;
}

Но в университете мне что-то рассказывали про ряды и их суммы, поэтому захотелось решить задачу математически. Как считать ряды я давно забыл, но ответ вы найдёте, перейдя на Wolfram Alpha и введя sum n^3, n = 1 to k в поле поиска. Узнаем, что:

\sum_{n=1}^{k} n^3 = \frac{1}{4}k^2(k + 1)^2

Приравняем правую часть к m и решим квадратное уравнение:

k^2 + k - 2\sqrt{m} = 0

x = \frac{\sqrt{1 + 8\sqrt{m}} - 1}{2}

Отсюда следует, что задача решается только если \sqrt{m} будет целым. Пишем код:

public static long findNb(long m) {
    double res = Math.sqrt(m);
    if (res - Math.floor(res) != 0) {
        return -1;
    }
    double d = 1 + 8 * res;
    double n = (Math.sqrt(d) - 1) / 2;
    return n - Math.floor(n) == 0 ? (long) n : -1;
}

Оказывается, что для некоторого числа тестовых данных, код даёт неверный ответ. Например, для значения 1646842954019151682L. В джаве корень из этого числа даёт:

System.out.println(String.format("%.30f", Math.sqrt(1646842954019151682L)));
// output 1283293791.000000000000000000000000000000

Тогда как правильный ответ:

1283293791.00000000038962239473657673134031176401122912...

Очевидно, что ответы не совпадают. Fin!

Вывод

Более правильное (и, наверняка, быстрое) решение не всегда даёт правильные результаты. Этим не удивишь опытного разработчика, но начинающие программисты долго могут гадать о природе ошибки.

И напоследок. Следующее выражение истинно:

System.out.println(1646842954019151681L == 1646842954019151682D);

а это ложно:

System.out.println(1646842954019151681L == (long) 1646842954019151682D);

Джон Лилли: Симуляции Бога

Когда человек проецирует Бога вовне и верит, что эта проекция реальна, — она становится реальной. Одной из побуждающих причин для такой проекции может быть то, что человек ещё не готов принять на себя ответственность быть Богом, в Боге и от Бога. Удобно проецировать своего Бога и делать из Него источник причинности и постоянства, Создателя человека и вселенной. Такой подход имеет определённые преимущества перед представлением о том, что человек принимает участие в развёртывании вселенной, которая есть Бог, и в которой сам человек — Бог: он позволяет переложить ответственность на кого-то другого и исполнять ту программу, в правильность которой верит группа людей.

https://batenka.ru/explore/lectures/john-lilly/

Сибирь 3: утомительно, плохо, скучно

Я прошёл эту игру. Нет, не так. Наконец-то я прошёл эту игру! И она отвратительная. У меня есть вопросы. Несколько.

Вопрос 1.

Зачем было менять классическое управление с point-to-click на адаптированный для консолей выкидышь трёхмерных игр? И если так уж хотелось именно 3D, почему не взяли механику от Тени судьбы/Shadow of Destiny/Shadow of Memories. Она была хорошей для начала 2000-х. Вместо этого Кейт управляется с фиксированной камеры. Камера, конечно, следует за ней при движении, но существует места, где это совершенно никак не помогает Кейт перемещаться. Поэтому она застревает, например, в какой-нибудь бочке на рынке или скамейке на корабле.

Непродуманная смена локаций вызывает только разражение: бежим направо, добежали до лестницы, переключились на новую локацию. Всё ещё наклоняете левый стик направо? Зря, вы вернулись на предыдущую локацию, но поняли это слишком поздно. И, будьте добры, подождите минуту, пока локация загрузиться (квест должен быть сложным!).

Поэтому игра тормозит. Все эти камеры, движение рывками и долгое ожидание смены локаций заставили меня и поскучать и побеситься.

Вопрос 2.

Кто это переводил? Перевод и субтитры сделаны в лучших традициях кустарного перевода аниме.

Вопрос 3.

Почему геймплей ужасен?

Первое, что сразу раздражает, — прокрутка рюкзака. Рюкзам сделан в виде списка, а новые предметы помещаются в конец. Чем больше предметов соберёте, тем дольше вам его крутить. Ну почему нельзя было класть новые предметы в начало этого списка?

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

Кстати, из-за управления я долго не мог понять, как включить ледорубы. Издалека мне было совсем не видно, что над кнопкой есть стеклянный предохранитель, которое нужно было сначала отодвинуть. Узнал я это, кстати, из прохождения на ютубе. Вообще, ближе к концу игры я всё чаще лазил за подсказками ради того, чтобы понять, где искать какую-нибудь мелочь. Как, например, шестерёнку для колеса обозрения. Сколько раз я бегал через комнату с инструментами в парке развлечений, но ни разу не замечал коробки, где и лежала эта чёртова шестерёнка.

Так что

Если вы поклоник 1 и 2 частей, то не покупайте и не играйте в 3. Хотя, лучше вообще не покупайте 3-ю часть: концовка ужасная, а сюжет в целом слабый и вымученный. Главные злодеи есть, чтобы были. Юколы бесполезны и на протяжении всей игры орут: «Помоги, Кейт Уока». А в заключительной части они всё равно вас кинут.

P.S. СПОЙЛЕР: в игре Оскар оживает. И есть кусок игры, где нам дают за него поиграем. Только движения у этого автоматона такие же, как и у Кейт. На этом всё.

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: