Страницы

Поиск по вопросам

Показаны сообщения с ярлыком переменные. Показать все сообщения
Показаны сообщения с ярлыком переменные. Показать все сообщения

четверг, 2 апреля 2020 г.

Удалить из переменный часть текста

#javascript #jquery #переменные

                    
Есть переменная fileWay - это пути к загруженным файлам на сервере разделенные символом
";" тегом перевода строки пример:

/home/j/23412353rt2/sait.ru/public_html/uploads/файл.pdf;
/home/j/23412353rt2/sait.ru/public_html/uploads/файл.pdf;
/home/j/23412353rt2/sait.ru/public_html/uploads/файл.pdf;
/home/j/23412353rt2/sait.ru/public_html/uploads/файл.pdf;
/home/j/23412353rt2/sait.ru/public_html/uploads/ - этот путь у всех файлов одинаковый, но количество и название файлов может меняться. В итоте нужно получить что-то вроде https://sait.ru/uploads/файл.pdf;
https://sait.ru/uploads/файл.pdf;
https://sait.ru/uploads/файл.pdf;
https://sait.ru/uploads/файл.pdf;
https://sait.ru/uploads/файл.pdf;
https://sait.ru/uploads/файл.pdf;
https://sait.ru/uploads/файл.pdf;
пробовал код ниже но он только для одного файла. как его адаптировать для моего случия? var srcfileWay = fileWay; var element = srcfileWay.split('/'); newfileWay = 'https://sait.ru/uploads/' + element[element.length - 1] Да, это повтор вопроса, не судите строго, но с первого раза я не разобрался, а при редактировании уже ни кто не отвечал на мой вопрос. (надеюсь на понимание)


Ответы

Ответ 1



Все вхождения фиксированной строки можно заменить другой фиксированной строкой с помощью метода replace и регулярного выражения: var fileWay = "/home/j/23412353rt2/sait.ru/public_html/uploads/файл.pdf;
/home/j/23412353rt2/sait.ru/public_html/uploads/файл.pdf;
/home/j/23412353rt2/sait.ru/public_html/uploads/файл.pdf;
/home/j/23412353rt2/sait.ru/public_html/uploads/файл.pdf;
"; var result = fileWay.replace(/\/home\/j\/23412353rt2\/sait\.ru\/public_html/g, "https://sait.ru"); console.log(result);

четверг, 19 марта 2020 г.

Как на Си в целой переменной (регистр) изменить какой либо бит?

#c #переменные #cpp


Нужно на Си изменить в целой переменной изменить бит, так чтобы не затронуть остальные?    


Ответы

Ответ 1



Есть несколько вариантов. Но по моему так будет лучше всего: // Присваиваем BIT2 - (0?1 << 2), вместо 2 номер любого бита с которым хотите работать! #define BIT2 (0?1 << 2) static int per; //Ваша переменная void set_bit2(void) //Функция которая будет устанавливать Ваш бит { per |= BIT2; //Установка соответствующего бита } void clear_bit2(void) //Функция которая будет затирать Ваш бит { per &= ~BIT2; //Стираем соответствующий бит } Как то так, но если хотите можете вспомнить битовые поля ))

четверг, 27 февраля 2020 г.

Что в Java является константой, а что - нет?

#java #переменные #интерфейс #константа


До недавнего времени я был почти на 100% убеждён в том, что прекрасно понимаю сущность
констант в языке программирования Java. Так было, пока я не наткнулся на один из примеров,
который был представлен в JLS (Java Language Specification). Он немного поверг меня
в ступор, и я не могу разобраться с этим вопросом вплоть до текущего момента.

Наверное, я не сильно ошибусь, если скажу, что типичных констант в языке программирования
Java нет вообще (во всяком случае, если смотреть с колокольни какого-нибудь С++). Даже
ключевое слово const, которое зарезервировано с незапамятных времён, но до сих пор
не используется, как бы ненавязчиво намекает нам об этом (не уверен, но вроде бы это
было сделано для того, чтобы сохранить обратную совместимость с С++ в том случае, если
константы всё-таки будут введены в язык).

В качестве некоторого общепринятого суррогата используется связка модификаторов static
final в процессе декларирования переменной. Если я правильно понимаю, то именно такие
переменные и считаются константами с точки зрения программистского сообщества ЯП Java.
В то же время, если заглянуть на просторах JLS в параграф, который посвящён инициализации
классов и интерфейсов, мы можем наткнуться на один интересный момент. Небольшая выдержка
оттуда:


  Example 12.4.1-3. Interface Initialization Does Not Initialize Superinterfaces


interface I {
    int i = 1, ii = Test.out("ii", 2);
}
interface J extends I {
    int j = Test.out("j", 3), jj = Test.out("jj", 4);
}
interface K extends J {
    int k = Test.out("k", 5);
}
class Test {
    public static void main(String[] args) {
        System.out.println(J.i);
        System.out.println(K.j);
    }
    static int out(String s, int i) {
        System.out.println(s + "=" + i);
        return i;
    }
}



  This program produces the output:
  
  1
  j=3
  jj=4
  3 
  
  The reference to J.i is to a field that is a constant
  variable (§4.12.4); therefore, it does not cause I to be initialized
  (§13.4.9).
  
  The reference to K.j is a reference to a field actually declared in
  interface J that is not a constant variable; this causes
  initialization of the fields of interface J, but not those of its
  superinterface I, nor those of interface K.
  
  Despite the fact that the name K is used to refer to field j of
  interface J, interface K is not initialized.


И вот этот момент мне совершенно непонятен! В интерфейсах все поля неявным образом
помечаются как public static final (другими они и не могут быть), что мы и видим в
приведённом примере. Все поля имеют совершенно одинаковый набор модификаторов, но при
этом, поле J.i называют константной переменной, а K.j константой уже не является, если
верить примеру, который приведён в JLS.

Что же такое константа с точки зрения JLS? Неужели это не абсолютно все статические
финализированные поля? Правильно ли я понял, что при присваивании значения через его
возврат функцией, мы уже не можем считать поле константой?
    


Ответы

Ответ 1



С точки зрения JLS: A constant variable is a final variable of primitive type or type String that is initialized with a constant expression То есть константа - это final переменная (причем речь не только о static и не только о полях) примитивного типа или типа String, которая проинициализирована константным выражением Вызов метода константным выражением не является, поэтому K.j - это не константа

вторник, 25 февраля 2020 г.

Размеры типов данных в Java

#переменные #java #книги


В книге П. Ноутона, Г. Шилдта "Java 2. Наиболее полное руководство" сказано следующее
о размерах типов данных:

О ширине (или количестве бит, отводимых для хранения значения) целочисленного типа
нельзя думать как о количестве памяти, которую он занимает, а скорее, как о поведении,
которое она определяет для переменных и выражений этого типа. Исполнительная среда
Java вольна использовать любой размер, какой она хочет, тогда как типы ведут себя согласно
их объявлению. Существует по крайней мере одна реализация исполнительной среды, которая
хранит числа byte и short как 32-разрядные (а не 8- и 16-разрядные) значения, чтобы
улучшить эффективность, потому что этим значением выражается размер слова большинства
используемых в настоящее время компьютеров.

В другом месте этой книги сказано:

Может показаться, что использование short или byte экономит память, но нет никакой
гарантии, что Java не будет внутренне так или иначе расширять эти типы до int. Помните,
что тип определяет поведение, а не размер. (Единственное исключение - массивы, где
тип byte гарантирует использование только одного байта на элемент массива, в то время
как short будет забирать два байта, а int - четыре байта).

В связи с вышеприведенным у меня возник вопрос: что подразумевается под словом поведение
переменных целочисленного типа и действительно ли, что для переменных, не объявленных
как массивы, размер не определен?
    


Ответы

Ответ 1



Полагаю, в данном случае мы вступаем в вечный конфликт: нам хочется думать о типах как о поведении (на уровне логики), но будучи сильно ограничены в ресурсах приходится думать еще и о количестве выделяемой памяти. В результате рождаются компромиссные варианты и компромиссные подходы думать о них. Например, во времена Си, по стандарту существовали только отношения типа short int <= int <= long int, все остальное являлось спецификой конкретной платформы. Это весьма шаткое положение вещей, которое было учтено в дизайне Java. Стандартом Java Language Specification четко заданы крайние значения для типов, то есть минимальная битность на физическом уровне. Например, long должен оперировать с 64-битными значениями, даже если нижележащая аппаратная платформа так не умеет. Кто угодно может написать свою "реализацию" JVM или языка Java, но если результат не будет удовлетворять спецификации назвать это "Java" уже нельзя, в спецификации вся суть технологии. Рассматривать проблемы с такими "реализациями" можно, но важно дистанцироваться от них, ведь это уже не проблемы Java-платформы, а проблемы какого-то стороннего решения. Можно ли это использовать как-то для определения реального железного перфоманса? Например, на 32-битной машине long, условно говоря, физически будет представлен двумя интами, то есть код работы с лонгами будет более медленный. С другой стороны, при переходе на 64-битную систему программа сама начинает работать медленней (до 20% на SPARC, 0-15% на AMD64 и EM64T) хотя бы из-за размера указателя увеличившегося с 4 до 8 байт. Иначе говоря, все сложно. В простом случае правильней держать в голове что размер примитивного типа - это поведение (гарантия на диапазоны значений и то, что между платформами логика работы с ними не развалится). Но помнить, что в конечном счете это прошитый внутрь стандарта инженерный компромисс, с которым в особо тяжких случаях придется что-то делать.

Ответ 2



Судя по всему под поведением авторы понимают то, как можно работать с этой этим типом данных, какие у неё границы, как можно конвертировать и т.п. По поводу второго вопроса: вам необходимо знать, что существуют несколько реализаций виртуальной машины (она называется сокращенно JVM) от разных компаний. Так вот в разных JVM при выполнении кода: int i = 1; переменная i может быть, либо 4, либо 8 байт. И это зависит от реализации JVM. Моё имхо и опыт подсказывают - пока вы не пишете программы, которые работают с миллионами записей, не стоит сильно на этом заморачиваться. Лучше осваивать программирование на java в целом. Когда подойдете к черте, где программы будут большими, вы уже будете знать, что с этим делать и как профайлить код.

воскресенье, 9 февраля 2020 г.

Пул констант в Java

#java #переменные #const #константа #pool


Не раз слышал о так называемом пуле констант в языке программирования Java. Знаю
о пуле объектов типа String, пуле для типов Byte, Short, Character, Integer, Long и
даже Boolean. Также знаю, что мы сами можем определять размер пула типа Integer, если
запускать приложение с параметром -Djava.lang.Integer.IntegerCache.high=XX, где XX
может колебаться в диапазоне от 127 до (Integer.MAX_VALUE - 129). Весь этот пул представляет
собой массив, каждое значение которого является элементом расположенным в порядке числового
возрастания и мы можем напрямую обратиться к нему по индексу за константное время O(1).
Я прекрасно понимаю, где хранится этот массив. У каждого целочисленного обёрточного
типа есть свой вложенный класс такого типа: 

private static class WrappingСlassNameCache {...}  


Где вместо WrappingСlassNameCache, мы подставляем конкретное имя класса, к примеру,
IntegerCache. 
Внутри это выглядит так: 

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }


Ну Бог с ним, тут ничего мудрёного нет! А что же такое пул констант? Что он из себя
представляет? И вообще где хранятся все константы в Java? Если мы говорим о локальных
финализированных переменных, то они хранятся в стековой памяти. Если мы имеем дело
со static final полями, то эти поля хранятся в MetaSpace (раньше в PermGen), если мы
говорим о реализации JVM HotSpot. Так о каком вообще пуле может идти речь? Как всё
это реализовано? Описано в каком-то классе, который поставляется вместе со стандартным
API от Oracle (раньше Sun Microsystems) или же это нужно заглядывать в реализацию конкретной
JVM и читать JVMS? Подскажите, пожалуйста, надоело, что в которой раз сталкиваюсь с
этим понятием и никак не могу понять о чём идёт речь. Всем огромное спасибо за помощь! :)
    


Ответы

Ответ 1



У каждого класса свой пул констант. Чтобы понять для чего он нужен, разберём простой пример class Example { public void hello() { System.out.println("Hello"); } } Скомпилируем $ javac Example.java И заглянем внутрь $ javap -c -v Example.class public class Example minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #6.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = Class #20 // Example #6 = Class #21 // java/lang/Object #7 = Utf8 #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 hello #12 = Utf8 SourceFile #13 = Utf8 Example.java #14 = NameAndType #7:#8 // "":()V #15 = Class #22 // java/lang/System #16 = NameAndType #23:#24 // out:Ljava/io/PrintStream; #17 = Utf8 Hello #18 = Class #25 // java/io/PrintStream #19 = NameAndType #26:#27 // println:(Ljava/lang/String;)V #20 = Utf8 Example #21 = Utf8 java/lang/Object #22 = Utf8 java/lang/System #23 = Utf8 out #24 = Utf8 Ljava/io/PrintStream; #25 = Utf8 java/io/PrintStream #26 = Utf8 println #27 = Utf8 (Ljava/lang/String;)V { public Example(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 1: 0 public void hello(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 } Первое, что представляет интерес - это байткоды метода hello: 0: getstatic #2 3: ldc #3 5: invokevirtual #4 Цифры до двоеточия обозначают байт, с которого начинается байткод и его параметры. По шагу видно, что каждая операция - это два байта, один байт на код операции и один байт на параметр. Байткоды могут принимать только параметры фиксированного размера - integer, long, short, byte, character, float, double, reference. Чтобы передать в метод println строку "Hello", надо загрузить в стек ссылку на строку "Hello", а саму строку где-то сохранить. Вот это где-то как раз и есть пул констант. А символы #3 после байткода ldc - ссылка на 3-й элемент в пуле констант. Constant pool: #1 = Methodref #6.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #17 // Hello #4 = Methodref #18.#19 // java/io/PrintStream.println:(Ljava/lang/String;)V Как можно увидеть из этого кусочка пула, его элементы сами часто ссылаются в пул. В частности наша строка под номером 3 ссылается на массив символов под номером 17. Если говорить о расположении пула констант в памяти JVM, то это один из участков MetaSpace.

суббота, 8 февраля 2020 г.

Возможно ли в переменную записать что угодно? C++

#cpp #переменные #шаблоны_с++


Допустим, пользователь должен ввести 2 аргумента, которые передадутся шаблону-функции,
которая принимает любой тип данных, там их меняет местами и выводит на экран... Изи. Но...
Как сделать так, чтобы пользователь через консоль мог вводить что угодно.
Скажем 1, 2. Или 5.2, 7.3 Или a, b.
Ведь то что он ввел, записывается в переменную. А переменная имеет свой тип. И если
она int, то пользователь не сможет ввести символ.
    


Ответы

Ответ 1



Входные данные вы получаете в виде строки. Тут без вариантов. Т.к. динамической типизации в C++ нет и вы не знаете заранее (по условию нужно чтобы мог вводить что угодно), что вам ввёл пользователь - этапа определения типов введённых данных не избежать. Вам для каждого аргумента надо определить, а не int ли это, или double. И в зависимости от результатов такой проверки уже решаете какую операцию применить к аргументам. Если они приводимы к числу. т.е. оба int или double, или один int а второй double то вы их складываете. Если хотя бы один из них не приводим к числу-работаете как со строками. Детали реализации это уже вкусовщина. Можно определять сразу после получения аргументов и, в зависимости от результатов проверки вызывать нужную функцию которая проведёт операцию и вернёт результат (можно и без функци обойтись) Можно после ввода вызывать функцию принимающую 2-а string'а и осуществляющую проверку и операции в теле функции. А можете создать класс наследуемый от string'a (сответственно чтоб его экземпляр можно было инициализировать строкой) и перегрузить в нём операцию "+". Тогда проверки и нужные операции будут выполняться в теле перегруженного метода.

Java. Задание занчения (final) переменной в try-блоке и ее дальнейшее использование и видимость

#java #переменные #инициализация #try_catch


Проблема заключается в необходимости задания значения final переменной (connectionSocket2)
в try-блоке. В дальнейшей части кода (в части run()) этого не видно и возникает как
бы ошибка, что та переменная не определена:

        final Socket connectionSocket;
        try { connectionSocket = welcomeSocket.accept(); }



  The local variable connectionSocket2 may not have been initialized


убрать final не могу, так как (в части run()) дает ошибку 


  Cannot refer to the non-final local variable connectionSocket defined
  in an enclosing scope


            Socket connectionSocket2=null;
            try { connectionSocket2 = welcomeSocket.accept();
            } catch (IOException e2) { e2.printStackTrace(); }
            final Socket connectionSocket=connectionSocket2; try{connectionSocket2.close();}catch(IOException
e1){e1.printStackTrace();}

            service.submit(new Runnable() {
                public void run() {
                    while (true) {
                        BufferedReader inFromClient=null;
                        DataOutputStream outToClient=null;
                        try{
                           inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
                           outToClient = new DataOutputStream(connectionSocket.getOutputStream());
                           outToClient.writeBytes(inFromClient.readLine());
                        } catch(IOException ioe) {} }}});


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

Приведенный код работает, использовал вторую переменную для передачи ей значения
и дальнейшего использования именной второй переменной. Но это выглядит плохо.

Как правильно, профессионально быть в этой ситуации? Модно ли как-то сказать компилятору,
что переменная на самом деле уже определена в try блоке и ему нечего "волноваться"?
(типа динамическая переменная, как в .Net)
    


Ответы

Ответ 1



Ответ-вопросник и очередной наброс на медальку. Итак, у вас было что-то вот такое: public class ICanIntoSockets { public static void main( String[] args ) throws IOException { ServerSocket welcomeSocket = new ServerSocket(10000); ExecutorService service = Executors.newCachedThreadPool(); doWork( welcomeSocket, service ); } public static void doWork( ServerSocket welcomeSocket, ExecutorService service ) { try { while (true) { final Socket connectionSocket = welcomeSocket.accept(); service.execute( () -> { try ( Socket socket = connectionSocket; BufferedReader inFromClient = new BufferedReader( new InputStreamReader(socket.getInputStream())); DataOutputStream outToClient = new DataOutputStream( socket.getOutputStream())) { outToClient.writeBytes(inFromClient.readLine()); } catch (IOException ioe) { ioe.printStackTrace(); } }); } } catch ( IOException ex ) { ex.printStackTrace(); } } } но try - плохо, и торморзит на тысячах подключений (тесты где?), поэтому вы решили от него избавиться. Ява - убогий язык, в ней зачем-то придумали Checked Exceptions и напихали во все места в стандартной библиотеке, поэтому совсем без try - никак: public static void doWork( ServerSocket welcomeSocket, ExecutorService service ) { while (true) { final Socket connectionSocket; try { connectionSocket = welcomeSocket.accept(); } catch (IOException ex) { ex.printStackTrace(); } service.execute(() -> { try ( Socket socket = connectionSocket; // The local variable connectionSocket may not have been initialized BufferedReader inFromClient = new BufferedReader( new InputStreamReader(socket.getInputStream())); DataOutputStream outToClient = new DataOutputStream(socket.getOutputStream())) { outToClient.writeBytes(inFromClient.readLine()); } catch (IOException ioe) { ioe.printStackTrace(); } }); } } Зло загнано в угол в одной строчке кода! Но компилятор почему-то считает, что переменная connectionSocket может быть не инициализирована. Почему? Потому что есть путь выполнения программы, при которой она действительно не инициализируется: когда welcomeSocket.accept() выбрасывает исключение. Метод не возвращает значение - значение переменной не присваивается. Что же делать? Не надо продолжать выполнение итерации, ваш код все равно не сможет работать дальше без клиентского сокета. Сделайте внутри catch-блока return, break, continue (в надежде, что следующий accept не выбросит исключение, что вряд ли). Если код непременно должен продолжаться дальше - присвойте connectionSocket null и где-то сделайте проверку. Предложенный вариант с массивом - это либо то самое создание объектов и выделение памяти, с которым вы сражаетесь, либо потеря подключений и обработка одного подключения несколько раз, смотря где вы этот массив объявите.

Ответ 2



Можно обёртку сделать: public class MySocket{ private Socket mSocket; public MySocket(){ } public void setSocket(Socket socket){ mSocket = socket; } public Socket getSocket(){ return mSocket; } } И создать его экземпляр: final MySocket connectionSocket = new MySocket(); И дальше: try{ connectionSocket.setSocket(welcomeSocket.accept()); } catch (IOException e2) { e2.printStackTrace(); } И внутри Runnable обращаться к connectionSocket.getSocket().

Ответ 3



создайте final Socket[] на один элемент и в пишите в него.

среда, 5 февраля 2020 г.

Как добавить путь в переменную $PATH?

#переменные #linux


Как добавить в переменную $PATH в linux путь к gcc компилятору (/usr/gcc/4.4/bin)?     


Ответы

Ответ 1



А точно нужно добавлять? Если все установили правильно, то должно работать. Но если хочется, то кто мешает открыть файл ~/.bashrc и добавить где то в конец пару строк: PATH=$PATH:/usr/gcc/4.4/bin export PATH

Ответ 2



Ответ посовременней: # ~/.bashrc export PATH="$PATH:/usr/gcc/4.4/bin"

пятница, 31 января 2020 г.

Передача переменных между функциями javascript

#функции #переменные #javascript


Например две функции:
var func1 = function(var1, var2,..., varN) {
    var first  = var1 + var2;
    var second = first + varN;
    ....
}

var func2 = function() {
}

как-то можно передать переменные, например first во вторую функцию не объявляя её
глобальной?    


Ответы

Ответ 1



Как вариант так: var func1 = function(var1, var2,..., varN) { var first = var1 + var2; var second = first + varN; .... return first; } var func2 = function(getFirst) { var first = getFirst(var1, var2,..., varN); } Вообще, функция должна что-то принимать и что-то возвращать. Лучше будет даже так: var func1 = function (args) {var first; .....; return first}, func2 = function (first)) {}; func1(func1(args)); Во избежания такой некрасивой вложенности лучше использовать временные переменные: var func1 = ...., func2 = ......, first = func1(args); func2(first)

Ответ 2



Например: Вернуть переменную из одной функции, получить во второй: function f1(){ var trololo = 345; return trololo; } function f2(){ var myTrololo = f1(); } Объявить вторую функцию внутри первой : function f1(){ var trololo = 345; function f2(){ var myTrololo = trololo; } } Использовать для связи какой-нибудь объект, к которому будут иметь доступ обе функции: var GodObj = {}; function f1(){ var trololo = 345; GodObj.lala = trololo; } function f2(){ var myTrololo = GodObj.lala; } Собственно вызвать вторую функцию из первой, с некоторым обычно необязательным параметром, но конечно тут зависит от того что эта функция делает function f1(){ var trololo = 345; f2(trololo); } functin f2(trololo){ if(typeof(trololo)!='undefined') myTrololo = trololo;// Если такой параметр есть }

rand() для переменных

#cpp #переменные #случайные_числа


Можно ли создать несколько переменных и при помощи функции rand() из этих переменных
выбрать случайным образом одну?
    


Ответы

Ответ 1



int a, b, c; int& x = (rand() > 3000) ? a : (rand() < 2000) ? b : c; Устроит? Это если вам нужна именно переменная - с возможностью записи и т.д. Если ее значение - и того проще, int x = (rand() > 3000) ? a : (rand() < 2000) ? b : c; Естественно, rand() нужно использовать с умом; тут я привел просто для примера... Это все имеет смысл, когда у вас уже есть переменные, из которых нужно делать выбор. Если вы сами их создаете... конечно, можно работать с массивом, вектором и т.д., но есть у меня подозрение, что это вопрос о том, какой рукой держать микроскоп при забивании гвоздей. Вам точно нужны именно переменные и выбор одной из них?

Ответ 2



Можно сделать через массив: std::vector v(5, 0); // устанавливаем значения в массиве int value = v.at(rand() % 5); Такой способ позволяет оформить все кратко, если предполагается большое кол-во переменных.

воскресенье, 26 января 2020 г.

Как отличить переменную от метода в С#?

#c_sharp #переменные #методы


Изучаю основы. Вычисление объема сферы, фрагмент кода.

public double sqrSphere()
{
    return 4 * Math.PI * r * r;
}

public void writeResult()
{
    Console.WriteLine("Вычислить площадь или объем? s/v:");
    s = Console.ReadLine();
    s = s.ToLower();
    if (s == "s")
    {
        Console.WriteLine("Площадь шара равна {0:#.###}", sqrSphere());
        return;
    }
}


Вопросы:

public double sqrSphere() 


sqrSphere() это метод или переменная? Если метод, то как можно сделать такой вывод?
По наличию параметров после идентефикатора? Раньше знал о переменных double, а здесь
это часть метода?

Console.WriteLine("Площадь шара равна {0:#.###}", sqrSphere());


Здесь выводится значение sqrSphere() . Получается, sqrSphere() - переменная?
Совсем запутался, простите нуба.

P.S.  Всем спасибо за ответы. Есть недостаток знаний о возвращаемых типах и методах
в целом, постигаю азы.
    


Ответы

Ответ 1



Любой метод имеет сигнатуру (собственное имя + список формальных параметров), тип возвращаемого значения, а так же возможно и различные модификаторы. public double sqrSphere() - открытый метод без параметров, возвращающий значение типа double. В этой строке Console.WriteLine("Площадь шара равна {0:#.###}", sqrSphere()); сначала вычисляется результат выполнения метода, а потом этот результат подставляется в форматную строку. За ходом исполнения программы вы можете наблюдать в отладчике.

Ответ 2



небольшое дополнение к уже имеющимся правильным ответам. Действительно иногда бывает трудно только по внешнему виду отличить переменную от свойства, метода или даже типа. Особенно если код чужой, мудреный и открыт в блокноте без подсветки синтаксиса. Поэтому соглашения об именовании становятся еще более актуальными. Понятно что почти у каждой команды есть свои соглашения (camelCase, PascalCase, венгерская нотация и т.д. останется за кадром, религиозные вопросы не трогаю принципиально =)), но некоторые из них можно считать общепринятыми. Ниже примерный список таких соглашений для пользовательских идентификаторов: длина имени должна быть необходимой и достаточной для однозначного понимания его смысла. Иначе говоря, не экономьте символы в ущерб понятности названия, но и не растекайтесь мыслью. Если есть общепринятая (или принятая в вашей команде) аббревиатура (usb, wcf и т.д.), ее можно и нужно использовать, но изобретать что-то свое только для уменьшения длины имени не стоит без крайней необходимости. имя поля или свойства является существительным простым Length, Size либо составным (да простят меня лингвисты за излишнее упрощение) CurrentTime, CustomerList. Для логических полей и свойств допустимо использовать вопросительную форму (IsActive, HasValue). имя метода является глаголом Count(), Sort() или начинается с глагола GetValue(), PerformAction(). Даже этих простых правил именования, не вдаваясь в прочие аспекты Code Conventions, будет вполне достаточно, чтобы относительно комфортно читать код не разыскивая объявление сущности для опознания каждого нового идентификатора.

Ответ 3



Как выглядит разница: //Метод public double Var1() { return 0; } //Свойство public double Var2 { get { return _var3; } set { _var3 = value; } } //Переменная / поле public double _var3 = 0; Раньше знал о переменных double, а здесь это часть метода? В данном случае это не часть метода, а тип возвращаемого значения этим методом. А иногда бывает так: public double sqrSphere() { return 4 * Math.PI * r * r; } public void Print (string str, Func func) { Console.WriteLine(str, func()); } public void writeResult() { Console.WriteLine("Вычислить площадь или объем? s/v:"); s = Console.ReadLine(); s = s.ToLower(); if (s == "s") { Print("Площадь шара равна {0:#.###}", sqrSphere); return; } } Немного глупый пример на коленке, но тут можно увидеть что метод sqrSphere в метод Print передается по виду совсем как переменная/поле/метод и для того, что бы выяснить что перед нами нужно знать сигнатуру метода Print и все станет ясно.

Ответ 4



Ну и совсем уж простейшая деталь: метод без "()" не вызвать. Если видишь имя без "()" - значит, это поле. public double sqrSphere() - а если анализировать эту строку, то public - идентификатор доступа, double - тип, возвращаемый методом, sqrSphere - имя метода. Ну и "()" для принимаемых аргументов.

Ответ 5



Может быть будет понятнее то что в IL коде получается при выводе строчки в консоль? IL_0029: ldstr "Площадь шара равна {0:#.###}" IL_002e: call float64 YourNameSpace.YourClass::'sqrSphere'() IL_0033: box [mscorlib]System.Double IL_0038: call void [mscorlib]System.Console::WriteLine(string, object) call говорит о том что это вызов метода. для переменных только загрузка (команды начинающиеся с ld)

среда, 22 января 2020 г.

Поле и/или переменная? Вот в чем вопрос. (Java)

#java #ооп #переменные


По ходу моего обучения языка java термин "поле" не дает мне покоя. Я ни в книгах,
ни в сети не нашел понятного объяснения этого явления. Кто-то говорит, что "поле" -
это "переменная", просто название другое (для примера: объект и экземпляр). В других
источниках - это разные вещи. 

Так вот, что такое "поле"? Дополнил примером. Он верный?

class MyClass{

int weigth = 50; //---поле
int height = 100; //---поле 
String str; //---поле

int myMethod(){

    int a; //---локальная переменная (НЕ поле)
    int b; //---локальная переменная (НЕ поле)
    str = "Это строка"; //---полю присваивается значение

    ...
}}

    


Ответы

Ответ 1



Понятие "поле" отнюдь не является чем-то новым в Java. Оно скорее переехало из других языков, существовавших задолго до Java. Так для меня "поле" всегда ассоциировалось с названием колонки в таблице. В Java объекты имеют определенный тип, который может быть классом, интерфейсом, или перечисляемым типом. Внутри этих объектов мы можем определять переменные, которые определяют состояние этих объектов. И наподобие с таблицами, которые имею названия полей для каждой записи, мы имеем названия переменных для каждого объекта класса. Не исключено, что внутри класса вы можете создавать другие переменные, например локальные переменные, или переменные параметры, которые видны в пределах блока или метода и тем самым не могут повлиять на состояние объекта. Такие переменные не являются полями. Более подробно можно почитать здесь. Понятие "поле" также имеет широкий спектр значений принятых в информатике и означает место, где находятся данные.

Ответ 2



На английском языке 2 разных терминов: field - для атрибута (переменной) в классе, array - для агрегатной переменной (переменной с многими значениями, доступными через индекс). Оба эти термины переводятся как поле. И так ваш пример может быть верным или нет - но в его контексте можно его считать верным.

вторник, 7 января 2020 г.

Переменные в функции

#переменные #javascript


var title = "external title";
function example(){
    title = "changing external title";
    alert(title);
    var title = "internal title";
}

example();
alert(title);

Почему "при запуске кода сгенерируются сообщения "changing external title", а затем
"external title", показывающее, что внешняя переменная title не была изменена внутри
функции. " Разве title внутри функции не глобальная?    


Ответы

Ответ 1



Нет, вы же пишите в последней строке var title, а интерпретатор - двухпроходной, сначала он ищет объявления типа function name(){}, var name в объявляемой области видимости и делает эти переменные локальными, а потом исполняет код. Благодаря этому поведению можно писать function Foo() { console.log('Me not run'); } function(){ var a=[Foo,Bar]; a[0](); function Foo() { console.log("Hello Foo"); } function Bar() { console.log("Hello Bar"); } }(); вас же не удивляет что Foo и Bar локальны для этой области видимости и что a[0] не undefined ? единственное что нужно помнить, что если вы объявляете функцию, то она станет сразу определена, еще на первом проходе, а если переменную, то правая часть объявления будет выполнена только во втором проходе.

воскресенье, 5 января 2020 г.

с++ использование единиц измерения для указания значений переменных

#cpp #cpp11 #переменные #cpp14 #преобразование_типов


Подскажите, как современный стиль программирования на C++ рекомендует использовать
единицы измерения при указания значений переменных?

Мне совершенно не нравится использование #define, поскольку очень часто теряется
смысловая нагрузка таких переменных, не видная привязка к конкретному классу (где это
может быть использовано), да и просто не очень эстетично

#define _KBps 1024.0


Поэтому я использую статические константы. Например для класса, который работает
с объектами в секунду и пользователь [кода] может указать определённые границы, я делаю так:

class CMyClass
{
public:
    static const double m_Ops;
    static const double m_KOps;
    static const double m_MOps;

// основная начинка класса
};

const double CMyClass::m_Ops = 1;
const double CMyClass::m_KOps = 1000.0;
const double CMyClass::m_MOps = 1000000.0;


CMyClass(2.0 * CMyClass::m_KOps, 1.34 * CMyClass::m_MOps);


Что уже (на мой скромный взгляд) более наглядно и удобно.

Но наверное С++1x  в этом отношении шагнули дальше и можно указывать 

CMyClass(2.0KOps, 1.34MOps);


?

Так ли это (вроде что-то подобное видел)?
И вообще какой подход предпочтителен?
    


Ответы

Ответ 1



См. п. 13.5.8 стандарта - литералы, определенные пользователем. Вот простейший пример: long double operator ""_MOps(long double x) { return x*1e6; } auto main() -> int { cout << 2.0_MOps << endl; }

Передача переменных в подключаемый файл

#файлы #php #переменные


Доброго всем времени суток господа
include('./block.php'); // работает
include('./block.php?alfa=1&beta=2'); // не работает

Собственно не могу передать get переменную в подключаемый файл. Айфреймы в моем случае
не катят. В гугле пишут, что 'должно работать'. Но в реальности ошибка
warning: include(./block.php?alfa=1&beta=2)
failed to open stream

короче как бы он пытается найти не файл с переменной, а файл с таким адресом
Подскажите, как прописать GET параметры в подключаемый файл.    


Ответы

Ответ 1



я к тому, что вашем первом посте оно подключается локально, а так нельзя, потому что оно будет искать файл с таким именем. ну да неважно уже. curl конечно потянет, какая ему разница? тем более курлом можно как GET, так и POST отдавать параметры. Попробуйте использовать не include , а file_get_contents, в вашем случае это будет более оправдано, да и более правильно... ps ни на одном своем сервере не держу пока 5.4, поэтому попробовать не могу. pps; вы файл все же будете удаленно подключать или все же с локальной машины?

Ответ 2



Третий пункт:

Ответ 3



Вот так же работает: //index.php //exemple.php http://...../index.php?yahoo=10

Ответ 4



Я так понимаю, в GET-запросе к 1.php ему передаются некие параметры, на основе которых он генерирует другие параметры и передаёт их подключенному 2.php, который, в свою очередь, оперирует с полученными данными и на их основе что-то выводит. Так почему бы не поместить логику 2.php в функцию с соответствующими методами и не вызывать её из 1.php? // 1.php // 2.php

пятница, 3 января 2020 г.

Как правильно присваивать переменную?

#javascript #переменные


Почему такая конструкция работает:

let outputData = document.querySelector('#outputData');
outputData.innerHTML = 'Введите значение';


А вот такая уже нет:

let outputData = document.querySelector('#outputData').innerHTML;
outputData = 'Введите значение';


Складывается впечатление, что методы нельзя добавлять в переменную. Или возможно
я не до конца понимаю особенности присваивания переменной.

Кто может объяснить эту ситуацию?
    


Ответы

Ответ 1



Складывается впечатление, что методы нельзя добавлять в переменную. Методы можно добавлять в переменную, однако, в данном случае в коде нет методов. innerHTML - это свойство, возвращающее строку. возможно я не до конца понимаю особенности присваивания переменной. Особенностей нет: присваивая что-то в переменную, никак не влияет на предыдущее значение переменной. var a = {}; a = 'a'; a = 3; Во всех трех случаях предыдущее значение теряется и никак не меняется. Почему работает первый вариант? В первом варианте: outputData.innerHTML = 'Введите значение'; Нет присваивания переменной. Есть изменение значения свойства объекта. При этом, .innerHTML еще и особое свойство, которое разбирает переданную строку и генерирует нужный html, который затем и отображается.

Ответ 2



querySelector возвращает ссылку на объект, а innerHTML - строка, базовые типы в js не ссылочные, поэтому когда вы приравниваете строке, то переменная будет типа string, а он базовый и ее изменение не будет менять ее в первоначальном месте. Например: const simpleType = 'string' let simpleType2 = simpleType simpleType2 = 'another string' // should be false console.log(simpleType === simpleType2) const obj = { string: 'string' } const obj2 = obj obj2.string = 'another strig' // should be true console.log(obj.string === obj2.string)

четверг, 2 января 2020 г.

Локальные переменные в Java

#java #ооп #переменные #методы #типы_данных


Собственно говоря, суть моего вопроса можно свести к следующему: почему мы в обязательном
порядке должны присваивать начальное значение локальным переменным, чтобы у нас появилась
возможность их как бы то ни было использовать? Почему для локальных переменных отсутствуют
значения по умолчанию? С чем это может быть связано? 

И ещё один интересный момент, который напрямую связан с моделью памяти в языке программирования
Java. На каком этапе локальная переменная добавляется в стэк? На этапе объявления?
Или всё же на этапе присваивания начального значения? Ведь если мы изначально выделяем
какие-нибудь 4 байта для хранения значения типа int, то конкретное значение у нас уже
имеется (его будет составлять совокупность двоичных разрядов, которые мы уже зарезервировали
под переменную). В общем, хотелось бы разобраться во всех этих вопросах. Всем крайне
благодарен!
    


Ответы

Ответ 1



У компилятора просто нет возможности определить обращаешься ты к проинициализированой переменной в классе или нет, потому на всякий случай им даются стандартные значения. А вот для локальных переменных можно отследить, что она не была проинициализирована и кинуть ошибку. Изначально, объявив переменную это всего лишь указатель на участок памяти, и содержит она то, что было в этом участке памяти до этого, то есть мусор. Чтобы мусора там не было, переменной нужно присвоить значение. То есть, стандартное значение у переменных класса это скорее костыль, для избежания получения "мусора" из переменной. А для локальных можно кинуть ошибку на стадии компиляции

Вызов статической переменной из другой функции

#c #функции #переменные


Необходимо обратиться к статической переменной из другой функции. Пример в коде:

 void First(void)
 {
    static int a = 5;
 }

 void Second(void)
 {
    //Тут необходимо узнать значение переменной 'a' из функции 'Firts'
 }

    


Ответы

Ответ 1



Напрямую - никак. Область видимости статической переменной внутри функции ограничена самой этой функцией. Можно извернуться как-то так: int *First( void ) { static int a = 0; return &a; } void Second( void ) { my *First_A_Pointer = First(); } Но сама потребность в подобных извращениях говорит о том, что архитектура кода крайне непродумана, и её нужно менять. Без вариантов.

Ответ 2



Пример не такой уж надуманный. Вот такой серьезный :) источник - W. E. Brown, “Three -related proposals, v2,” Tech. Rep. N3742, ISO/IEC JTC 1, Information Technology, Subcommittee SC 22, Programming Language C++, August 2013. Автор предлагает для начинающих упрощенный набор функций для работы со случайными числами. Поскольку движок надо иметь один (понятно, почему? :)), а глобальные переменные - очень плохо, его решение именно такое - статическая переменная, к которой имеют доступ несколько других функций (упрощенный код взят из книги "Discovering Miodern C++"): #include std::default_random_engine& global_urng() { static std::default_random_engine u{}; return u; } void randomize() { static std::random_device rd{}; global_urng().seed(rd()); } int pick(int from, int thru) { static std::uniform_int_distribution<> d{}; using parm_t = decltype(d)::param_type; return d(global_urng(),parm_t{from,thru}); } double pick(double from, double upto) { static std::uniform_real_distribution<> d{}; using parm_t = decltype(d)::param_type; return d(global_urng(),parm_t{from,upto}); } Так что, как видите, не всегда это "крайняя непродуманность"...

среда, 1 января 2020 г.

Для чего делать финализированную переменную статической?

#java #переменные #константа #статические_члены #финализированные_переменные


Для чего делать финализированную переменную статической?
В смысле, в каких случаях это имеет смысл и почему нельзя обойтись без этого?
    


Ответы

Ответ 1



Если коротко - можно обойтись и без этого, но так оптимальнее по памяти в общем случае. static final принадлежит классу, соответственно не зависимо от количества экземпляров этого класса будет создан только один объект типа static final переменной. В случае с просто final каждый объект будет содержать ссылку на новую копию. Если такая переменная содержит ссылку на объект, а не на примитивный тип, значение которого может быть закешировано (intern) - память будет израсходована на хранение одинаковых объектов. —- Так же есть различия в жизненном цикле(порядок инициализации). Статика будет проинициализирована во время загрузки класса класс-лоадером. Т.е. даже если ни один экземпляр этого класса никогда не будет создан. Просто final же напротив - будет проинициализирована в момент создания экземпляра. UPD: Таким образом получается что для всяких там строк и других immutable объектов, можно ставить static, чтобы не плодить идентичные копии, однако для объектов, которые содержат в себе какое-то состояние, которое может быть изменено со временем - уже стоит подумать ставить static или нет. Т.е. наличие директивы final еще не говорит о том что объект не поменяется со временем. final это атрибут поля, но никак не объекта на который ссылается это поле.

понедельник, 30 декабря 2019 г.

Приоритеты операций в языке программирования Java

#java #переменные #операторы


Речь о приоритете операторов в Java.

Итак, уже не раз наталкиваюсь на такие вот интересные таблички, в которых операторы
выставлены в приоритете их выполнения в программе. Типичный пример: https://introcs.cs.princeton.edu/java/11precedence/ 
Можно поискать и другие варианты, но все они более-менее схожи. Во всех таблицах,
которые попадались мне на глаза, постфиксные унарные операторы инкремента и декремента
имеют явный приоритет над своими префиксными аналогами. Следовательно, стоило бы ожидать,
что в составном выражении, которое содержит, как постфиксный инкремент/декремент, так
и префиксный, изначально должен вычисляться именно тот инкремент/декремент, который
был записан в постфиксной форме. Хорошо, давайте тогда рассмотрим небольшой пример.
Возьмём следующий кусок кода: 

 int y = 10;
 int z = ++y * y--;
 System.out.println(z);


Что мы здесь имеем? Я вижу целых 4 операции в данном составном выражении. Ключевой
операций здесь является операция присваивания, но она будет выполнена в самую последнюю
очередь, так как имеет наименьший приоритет. В таком случае нам необходимо отдельно
рассмотреть правую часть данной операции. Очевидно, что изначально необходимо выполнить
унарные операции, а уже потом переходить к бинарной. Но какую операцию делать в первую
очередь? Это достаточно принципиально, так как от этого зависит результат выполнения
программы. Если верить таблице, то сначала я должен вычислить инкремент/декремент,
а потом уже переходить к их перемножению. Поскольку постфиксная форма имеет более высокий
приоритет, то сначала я провожу операцию y--, а уже потом ++y. В общем, если всё делать
так, как я это понимаю, то в результате должно получиться 100. Пишу данный код в своей
IDE и вывожу его на консоль. Результат: 121. Почему так? Неправильно расписана таблица
приоритетов? Или же я чего-то не понимаю?
    


Ответы

Ответ 1



Операция ++y выполняется непосредственно перед тем как значение y будет подставлено в выражение. Операция y-- сразу после. Пример 1: y= 10 z = ++y * y-- ---------------------------- y = y + 1 z = 11 * 11 // 11*11 = 121 y = y - 1 Пример 2: z = (y++) * (++y) ---------------------------- z = 10 * (++y) y = y + 1 y = y + 1 z = 10 * 12 // 10*12 = 120 UPD: JLS утверждает что существуют первичные выражения Первичные выражения включают в себя большинство простейших видов выражений, из которых строятся другие: литералы, создание объектов, обращения к полям, вызовы методов, ссылки на методы и обращения к массиву. Выражение в скобках также рассматривается синтаксически как первичное выражение. постфиксные выражения Постфиксные выражения включают использование операторов postfix ++ и -. Они не считаются первичными выражениями (§15.8), но обрабатываются отдельно в грамматике, чтобы избежать определенных неоднозначностей. И становятся взаимозаменяемыми только здесь, на уровне приоритета постфиксных выражений. и унарные операторы Оператор +, -, ++, --, ~, !, и оператор приведения типа (§15.16) называются унарными операторами. Видно что постфиксные операции занимают отдельную позицию в структуре выражений Java, где-то между вызовами методов и унарными операторами. Соответственно им должен быть назначен определенный уровень приоритета. JLS не объясняет конкретно, почему это было сделано, оправдываясь "определенными неоднозначностями". Можно предположить, что это относиться к удобству парсинга выражений или определенной реализации его механизма. Как выяснили в соседних ответах, столкнуться с конфликтом префиксных и постфиксных операторов или неоднозначностью выражения с их использованием достаточно сложно, и на практику написания кода такое разделение уровней приоритетов сильно не влияет.

Ответ 2



Чтобы разобраться, достаточно открыть .class файл и посмотреть как java его декомпилировала: int y = 10; int y = y + 1; // 11 int z = y * y--; // 11 * 11, потом y = y - 1, но это уже не важно, т.к. `y` больше не используется System.out.println("z = " + z);

Ответ 3



Значение подставляется сразу после выполнения операции. То есть, выполнив ++y оно вернет значение 11 на место операции, а в месте y-- сначала вернет 11, а потом уменьшит y на единицу. И выходит что 11*11 Я как-то даже не обращал внимание, что у постфиксных унарных операторов приоритет выше. Выполняются они все в порядке очереди. Интересно Почему оно так работает, вроде бы понятно. Приоритет нужен в случае решения конфликта, когда два оператора разных приоритетов сталкиваются на одном уровне. То есть (a + b*b) сразу ясно что выполнять первым необходимо умножение, а в случае (a+a == b*b) смысла выполнять первым именно операцию умножение нет, идя в порядке слева направо получим тот же результат. Приоритет операторов ++/-- помог бы решить ситуацию вроде (++a--), указывающий, что сначала нужно выполнить именно уменьшение переменной, но такая конструкция запрещена, да и смыла в ней мало. В случае с (++a * a--) выполнятся будет слева направо. А из=за того, что унарные операции изменяют саму переменную, мы можем проследить какая операция в действительности выполняется первая, хотя по сути это не должно иметь значения. Их нужно использовать с осторожностью

Ответ 4



Друзья, спасибо всем за помощь! Мозговой штурм действительно работает, убедился в этом на своей собственной шкуре. Знаю, что данная тема вводит в ступор многих начинающих программистов, поэтому решил расписать свои соображения на данный счёт. Надеюсь, что судить строго не будете. Итак, давайте начнём разбираться в этом вопросе. В чём основная проблема людей (в том числе и моя) в понимании данной темы? А проблема в том, что мы путаем «приоритет» с обычным порядком выполнения операторов. В каком порядке JVM выполняет инструкции? Очевидно, что все инструкции (а также операторы составных выражений) выполняются в привычном нам левоассоциативном направлении (слева направо), а когда мы доходим до конца строки (как правило в конце каждой строки ставится специальный Unicode-символ (или их комбинация), который является ограничителем строк (в основном это символы, которые имеют мнемоническое обозначение CR+LF)), то мы переходим на нижестоящую строчку и выполняем код с самого начала в том же левостороннем направлении (можно представить это как возврат каретки в самое начало строки в пишущей машинке, а затем перевод той же каретки в начало следующей строки). Когда же начинаются основные сложности? Все неурядицы возникают ровным счётом тогда, когда мы сталкиваемся с некоторой неопределённостью. Что я имею в виду? Давайте рассмотрим классический пример применения на практике таблицы приоритетов операторов. Возьмём следующее арифметическое выражение: /* * Some code */ int z = a + b * c; Ещё с начальной школы мы знаем, что сперва выполняются мультипликативные операции, а затем уже аддитивные, так как первые имеют явный приоритет над вторыми. Как это реализовано в самой Java'е и какая здесь может возникнуть коллизия? Мы видим в правой части операции присваивания составное выражение, которое состоит из двух бинарных арифметических операторов и 3-х операндов. Очевидно, что у нас есть два возможных пути, которые будут пересекаться в одном месте. Мы можем сделать так: (a + b) * с; Или же так: a + (b * c); Коллизия заключается в том, что в обеих случаях мы захватываем переменную b, которая одновременно является одним из операндов относительно обеих операторов. Понятное дело, что конечный результат будет разниться в зависимости от выбранного пути. Вот тут и вступают в дело приоритеты операторов! Мы прекрасно знаем, что операция умножения будет выполняться первой, ведь для нас это весьма очевидно и мы даже не обращаем на это внимания (хотя всё это изначально прописано в "мозгах" самой JVM). А теперь хотелось бы перейти к более сложному примеру, с которого всё и начиналось. Почему же инкремент/декремент записанный в постфиксной нотации имеет приоритет над префиксной формой той же записи? Настолько я понял, то в обеих случаях можно привести лишь по одному примеру, где может возникнуть явная неопределённость. Приведём эти примеры: a---b; a+++b; Такая форма записи допускает всего 2 возможных варианта без ошибки времени компиляции. Здесь можно выделить 2 оператора, один из которых будет бинарным оператором (сложения или вычитания), а также унарный инкремент/декремент (о форме записи пока ничего не говорим). Здесь уже возникает немного другого рода неопределённость, которая отличается от первого рассмотренного нами случая. Если в первом случае возникла коллизия на уровне совместно используемого операнда, то здесь возникает двусмысленность в вопросе унарного оператора, который может быть постфиксным/префиксным инкрементом/декрементом, как для переменной a, так и для переменной b. Имеем следующие варианты раскрытия скобок: (a--)-b; a-(--b); (a++)+b; a+(++b); Положение бинарного оператора имеет принципиальное значение только в первом случае, так как вычитание является антикоммутативной операций, а сложение, напротив, коммутативной. Хотелось бы добавить, что такой приоритет появился совсем не случайно. Чтобы понять это, необходимо обратиться к JLS: The longest possible translation is used at each step, even if the result does not ultimately make a correct program while another lexical translation would. There is one exception: if lexical translation occurs in a type context (§4.11) and the input stream has two or more consecutive > characters that are followed by a non-> character, then each > character must be translated to the token for the numerical comparison operator >. The input characters a--b are tokenized (§3.5) as a, --, b, which is not part of any grammatically correct program, even though the tokenization a, -, -, b could be part of a grammatically correct program. Without the rule for > characters, two consecutive > brackets in a type such as List> would be tokenized as the signed right shift operator >>, while three consecutive > brackets in a type such as List>> would be tokenized as the unsigned right shift operator >>>. Worse, the tokenization of four or more consecutive > brackets in a type such as List>>> would be ambiguous, as various combinations of >, >>, and >>> tokens could represent the >>>> characters. Как мы можем понять, на каждом этапе используется наиболее длинная трансляция, даже если она приводит к некорректной программе. Скорее всего именно по этой причине постфиксная форма унарных операторов имеет некоторый приоритет над префиксной, это вписывается в общую логику лексической транляции, тем более других примеров в которых могла бы возникнуть коллизия между такого рода операторами придумать не то чтобы трудно, а скорее всего невозможно (во всяком случае, лично я не вижу других возможных вариантов написания корректной программы, где можно было бы смоделировать данную ситуацию). Напоследок можно рассмотреть пример с логическими операторами. boolean bool = a ^ b & c | d; где a, b, c, d — переменные логического типа. Чтобы понять, какая операция будет выполняться первой, было бы неплохо заглянуть вот сюда. Зная приоритет каждого из операторов находим правильное решение: boolean bool = ((a ^ (b & c)) | d); Что хотелось бы сказать напоследок? В глобальной сети можно найти очень много ложных таблиц, которые могут ввести в заблуждение. Авторы добавляют туда всякую "отсебятину", по типу разделителей, оператора new, операторы приведения типов и прочую чепуху. Считаю, что есть единственный источник к которому можно обратиться в данном вопросе, это официальный сайт корпорации Oracle. И да, соглашусь с тем, что лучше использовать обычные скобки для коррекции приоритетов! :) Благодарю всех за внимание! ;)