Re[26]: C# 8 - фича №1
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.12.19 11:25
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Здравствуйте, samius, Вы писали:


S>>Но совпадающие границы у пустого массива — вполне приличная абстракция. Лучше, чем другие варианты, как по мне.


ARK>Ну а все же — почему лучше считать [2, 2] пустой последовательностью, чем последовательностью с одним элементом? В чем преимущество?

Я тут вижу один элемент — двухэлементный кортеж.

printf "%A" [2,2] 
// [(2, 2)]
Re[27]: C# 8 - фича №1
От: AlexRK  
Дата: 05.12.19 11:29
Оценка:
Здравствуйте, samius, Вы писали:

S>>>Но совпадающие границы у пустого массива — вполне приличная абстракция. Лучше, чем другие варианты, как по мне.


ARK>>Ну а все же — почему лучше считать [2, 2] пустой последовательностью, чем последовательностью с одним элементом? В чем преимущество?

S>Я тут вижу один элемент — двухэлементный кортеж.

Хорошо. Почему лучше считать "2 .. 2" пустой последовательностью, чем последовательностью с одним элементом? В чем преимущество?
Re[28]: C# 8 - фича №1
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.12.19 11:35
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Здравствуйте, samius, Вы писали:


ARK>Хорошо. Почему лучше считать "2 .. 2" пустой последовательностью, чем последовательностью с одним элементом? В чем преимущество?


Тут все-таки зависит от контекста. Точнее, от ширины элемента. На числовой прямой у нас элементы (числа) не имеют ширины, поэтому [2..2] там вырожденный одноэлементный диапазон. Но когда у элемента есть ненулевая ширина, то по понятным причинам в диапазоне 2..2 не поместится ни одного элемента. Можно даже считать это [2.0 .. 2.0]
Re[29]: C# 8 - фича №1
От: AlexRK  
Дата: 05.12.19 11:53
Оценка:
Здравствуйте, samius, Вы писали:

ARK>>Хорошо. Почему лучше считать "2 .. 2" пустой последовательностью, чем последовательностью с одним элементом? В чем преимущество?


S>Тут все-таки зависит от контекста. Точнее, от ширины элемента. На числовой прямой у нас элементы (числа) не имеют ширины, поэтому [2..2] там вырожденный одноэлементный диапазон. Но когда у элемента есть ненулевая ширина, то по понятным причинам в диапазоне 2..2 не поместится ни одного элемента. Можно даже считать это [2.0 .. 2.0]


От ширины можно абстрагироваться, считать все элементы атомарными. Это вопрос абстракции. Можно с равным успехом принять 2..2 за диапазон длиной как 0 элементов, так и 1 элемент.

У Дейкстры я увидел два аргумента в пользу первого варианта:
1) можно получить длину без прибавления "1";
2) пустой диапазон представляется "уродливо".

Я нахожу оба этих аргумента несостоятельными — длину надо получать не жонглированием границ диапазонов, а функцией "length" (лучше и читабельность, и поддержка), а "уродлив", на мой взгляд, как раз-таки пустой диапазон в виде "2 .. 2" (опять же, для читабельности и поддержки пустой диапазон проверить можно и нужно соответствующей функцией).

Все это — тяжелое наследие жонглирования битами из С. Зачем на высоком уровне это нужно — пока понять не могу.
Re[20]: Пустая последовательность
От: Qbit86 Кипр
Дата: 05.12.19 12:40
Оценка:
Здравствуйте, AlexRK, Вы писали:

advantage that the difference between the bounds as mentioned equals the length of the subsequence


ARK>С одной стороны верно, с другой — в математике испокон веку длина диапазона равна (end — start + 1).


Так «в математике» и нумерация часто с единицы начинается. В математике не приходится много возиться с буферами. А в программировании приходится, и там принят подход (start, endExclusive) или (start, count), где endExclusive = start + count. Без ±1. Если это не дремучий Фортран или Паскаль. Полузакрытые интервалы удобно конкатенировать (приставлять рядышком), в них можно выразить пустую последовательность, они не подвержены «ошибкам на единицу».

Consider now the subsequences starting at the smallest natural number: inclusion of the upper bound would then force the latter to be unnatural by the time the sequence has shrunk to the empty one.


ARK>Да и идея выражать пустой диапазон индексами довольно маразматична сама по себе, ИМХО.


Нет, это очень изящная и практичная идея. Невозможность в закрытом интервале выразить пустую последовательность — это серьёзный недостаток.
Глаза у меня добрые, но рубашка — смирительная!
Re[21]: Пустая последовательность
От: AlexRK  
Дата: 05.12.19 13:15
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>В математике не приходится много возиться с буферами. А в программировании приходится, и там принят подход (start, endExclusive) или (start, count), где endExclusive = start + count.


Я бы сказал скорее не "в программировании", а "в низкоуровневом жонглировании битами", что составляет не очень большую часть программирования.

Q>Полузакрытые интервалы удобно конкатенировать (приставлять рядышком)


А закрытые почему нельзя? range1.Concat(range2) или range1 + range2

Q>в них можно выразить пустую последовательность


range.IsEmpty()

Q>они не подвержены «ошибкам на единицу».


range.Length()

ARK>>Да и идея выражать пустой диапазон индексами довольно маразматична сама по себе, ИМХО.

Q>Нет, это очень изящная и практичная идея. Невозможность в закрытом интервале выразить пустую последовательность — это серьёзный недостаток.

Не вижу особого смысла в этой идее. Можно хотя бы иллюстративный пример задачи, которую она изящно и практично решает?
Re[22]: Примеры
От: Qbit86 Кипр
Дата: 05.12.19 13:48
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>range1.Concat(range2) или range1 + range2

ARK>range.IsEmpty()
ARK>range.Length()

Это всё вообще мимо кассы. Это внешний API. Как он устроен внутри? Как рейнджи создаются?

ARK>Можно хотя бы иллюстративный пример задачи, которую она изящно и практично решает?


Не, давай теперь ты примеры. На тех мифических языках, где в стандартных API рейнджи задаются закрытыми диапазонами. Начнём с D.
Глаза у меня добрые, но рубашка — смирительная!
Re[23]: End is not included
От: Qbit86 Кипр
Дата: 05.12.19 14:55
Оценка:
Q>Не, давай теперь ты примеры. На тех мифических языках, где в стандартных API рейнджи задаются закрытыми диапазонами. Начнём с D.

Ладно, я сам:
import std;

void main()
{
    foreach (int x; 2..6)
        writeln(x);

    foreach (int x; iota(2, 6))
        writeln(x);
}


Правая граница не выводится. Согласно документации:

Parameters:
B begin The starting value.
E end The value that serves as the stopping criterion. This value is not included in the range.
— https://dlang.org/phobos/std_range.html#iota

Глаза у меня добрые, но рубашка — смирительная!
Re[23]: Примеры
От: AlexRK  
Дата: 05.12.19 15:37
Оценка:
Здравствуйте, Qbit86, Вы писали:

ARK>>range1.Concat(range2) или range1 + range2

ARK>>range.IsEmpty()
ARK>>range.Length()
Q>Это всё вообще мимо кассы. Это внешний API. Как он устроен внутри? Как рейнджи создаются?

Это API рангов, обесценивающий рассуждения о нужности полуоткрытых диапазонов. Как он устроен внутри, не имеет значения — это деталь реализации. Создавать диапазон можно или как a..b, или через функцию а-ля CreateRange(a, b).

ARK>>Можно хотя бы иллюстративный пример задачи, которую она изящно и практично решает?

Q>Не, давай теперь ты примеры. На тех мифических языках, где в стандартных API рейнджи задаются закрытыми диапазонами. Начнём с D.

На D я уже приводил примеры выше, но у него не закрытые диапазоны.

Пример закрытого диапазона выглядит так же, как и пример полуоткрытого. Я просто пока не очень понял, в чем "изящество и практичность" полуоткрытых диапазонов. Если исключить низкоуровневое жонглирование битами, где оно может быть полезно, как и адресная арифметика.
Re[24]: End is not included
От: AlexRK  
Дата: 05.12.19 15:40
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>>Не, давай теперь ты примеры. На тех мифических языках, где в стандартных API рейнджи задаются закрытыми диапазонами. Начнём с D.

Q>Ладно, я сам:
Q>Правая граница не выводится. Согласно документации:

Я нигде не утверждал, что в D закрытые диапазоны. Там просто нет сбивающих с толку "индексов с конца". Это две разные вещи, хотя и смежные.
Re[24]: Примеры
От: Qbit86 Кипр
Дата: 05.12.19 15:59
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Это API рангов, обесценивающий рассуждения о нужности полуоткрытых диапазонов. Как он устроен внутри, не имеет значения — это деталь реализации.


Так а зачем тогда вообще притянули за уши сравнение с рангами в D? В C# сто лет уже такие ранги, называются IEnumerable<T>/Linq. Новые Index/Range — не про это.

ARK>Создавать диапазон можно или как a..b, или через функцию а-ля CreateRange(a, b).


И в обоих случаях правая граница не включается, правильно?

import std;

void main()
{
    foreach (int x; 2..6)
        writeln(x);

    foreach (int x; iota(2, 6))
        writeln(x);
}


Parameters:
B begin The starting value.
E end The value that serves as the stopping criterion. This value is not included in the range.
https://dlang.org/phobos/std_range.html#iota


ARK>Если исключить низкоуровневое жонглирование битами, где оно может быть полезно, как и адресная арифметика.


Вот мне недавно нужно было отформатировать кучу значений разных типов. И, чтобы не аллоцировать кучу строк, я форматировал всё в один общий массив char'ов. (В .NET Standard 2.1 для этого недавно появился API на Span'ах.) И размечен этот буфер аналогами ArraySegment, каждый внутри представлен парой (offset, count) с переходом в [start, end) при итерации.
И да, иногда некоторые значения — пустые строки, тогда их разметка — это пара (offset, 0) или [start, start) при итерации. Положение этого диапазона, хоть и пустого, в результирующем буфере не забывается, это тоже может использоваться.
Глаза у меня добрые, но рубашка — смирительная!
Re[30]: C# 8 - фича №1
От: samius Япония http://sams-tricks.blogspot.com
Дата: 05.12.19 16:20
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Здравствуйте, samius, Вы писали:


S>>Тут все-таки зависит от контекста. Точнее, от ширины элемента. На числовой прямой у нас элементы (числа) не имеют ширины, поэтому [2..2] там вырожденный одноэлементный диапазон. Но когда у элемента есть ненулевая ширина, то по понятным причинам в диапазоне 2..2 не поместится ни одного элемента. Можно даже считать это [2.0 .. 2.0]


ARK>От ширины можно абстрагироваться, считать все элементы атомарными. Это вопрос абстракции. Можно с равным успехом принять 2..2 за диапазон длиной как 0 элементов, так и 1 элемент.


Можно и сантиметры считать атомарными, с учетом того что расстояние между соседними всегда фиксированное. Но зачем?

ARK>Все это — тяжелое наследие жонглирования битами из С. Зачем на высоком уровне это нужно — пока понять не могу.

Затем, что жонглирование битами уже использовало старую абстракцию измерения, которая была задолго до битов. Затрудняюсь даже сказать, за сколько (тысячелетий).
Re[25]: Примеры
От: AlexRK  
Дата: 05.12.19 17:03
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Так а зачем тогда вообще притянули за уши сравнение с рангами в D?


Мне нравится в D то, что нет индекса с конца — есть символ "$", обозначающий конец массива (просто сокращение от длины). И, как следствие, нет некрасивой асимметричности "первый элемент a[0], последний элемент a[^1]".

ARK>>Создавать диапазон можно или как a..b, или через функцию а-ля CreateRange(a, b).

Q>И в обоих случаях правая граница не включается, правильно?

Прикол в том, что, если использовать API Concat/Length/IsEmpty/etc., то разницы между включением и не-включением правой границы нет. Ну, во всяком случае, навскидку я ее не вижу.

Q>Вот мне недавно нужно было отформатировать кучу значений разных типов. И, чтобы не аллоцировать кучу строк, я форматировал всё в один общий массив char'ов. (В .NET Standard 2.1 для этого недавно появился API на Span'ах.) И размечен этот буфер аналогами ArraySegment, каждый внутри представлен парой (offset, count) с переходом в [start, end) при итерации.

Q>И да, иногда некоторые значения — пустые строки, тогда их разметка — это пара (offset, 0) или [start, start) при итерации. Положение этого диапазона, хоть и пустого, в результирующем буфере не забывается, это тоже может использоваться.

Если я правильно понимаю, особой пользы от "обратных индексов" в данном примере нет.

Про полуоткрытый диапазон — опять же, если я правильно понимаю, указатели на подстроки в буфере идут с разрывами? Если бы было без разрывов, то одно из полей явно лишнее, достаточно было бы одного смещения, разве нет? А если с разрывами, то start при переходе определяется не просто предыдущим "offset + count", а более сложной формулой. А раз так, не вижу особой беды туда сунуть дополнительно +1, чтобы работать с закрытым диапазоном. Лично для меня так было бы понятнее. Кстати, я не понял, а как же захватывается последний символ буфера, он будет в последнем элементе указывать ЗА массив?
Re[26]: Off-by-one error
От: Qbit86 Кипр
Дата: 05.12.19 22:22
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Если я правильно понимаю, особой пользы от "обратных индексов" в данном примере нет.


Пользы от обратных индексов мне нет никакой, я их не использую. Речь ведь не про их пользу для меня, а про их консистентность, раз уж такая фича есть. Так вот, определение ^i как length — i консистентно с соглашением о полуоткрытых интервалах, а другой вариант неконсистентен. А соглашение о полуоткрытых интервалах само по себе важно, и непосредственно связано с нумерацией от нуля и избеганием «ошибок на единицу». Поэтому такой подход и распространён в современных языках, придуманных после Фортрана.

Если они вам тоже не нужны, добавьте в EditorConfig строчки:
csharp_style_prefer_index_operator = false:suggestion
csharp_style_prefer_range_operator = false:suggestion


ARK>Про полуоткрытый диапазон — опять же, если я правильно понимаю, указатели на подстроки в буфере идут с разрывами?


В этом примере да, кроме отформатированных объектов был ещё посторонний текст.

ARK>Если бы было без разрывов, то одно из полей явно лишнее, достаточно было бы одного смещения, разве нет?


Всё так, такое тоже было в моей практике. Например, один сплошной массив всех ребёр графа (сгруппированных и упорядоченных по source-вершине), и дополнительная разметка, какой поддиапазон этого массива какой вершине инцидентен. Там без разрывов, и exclusiveEnd одного диапазона совпадает с inclusiveStart следующего, поэтому просто смещения. Всё прекрасно стэкается друг с другом, ±1 нигде не фигурируют, пустые диапазоны (отсутствуют исходящие рёбра в некоторых вершинах) вполне естественно сохраняются как повторяющиеся start'ы. Для порождения энумератора по поддиапазону как раз удобно представление [start, end), а не (offset, count) как принято в API, чтоб избежать лишних сложений.

ARK>А если с разрывами, то start при переходе определяется не просто предыдущим "offset + count", а более сложной формулой.


В этом случае start при переходе определяется своим сохранённым offset, а не рассчитывается от offset'а предыдущего.

ARK>А раз так, не вижу особой беды туда сунуть дополнительно +1, чтобы работать с закрытым диапазоном.


Так +1 или -1? Это просто не нужно, это лишнее пространство для ошибок, не говоря уже про лишние вычисления. И отсутствие единообразия: если без разрывов, то используем «полуоткрытый» подход, а если с разрывами, то «закрытый» подход.

ARK>Кстати, я не понял, а как же захватывается последний символ буфера, он будет в последнем элементе указывать ЗА массив?


Не вижу проблемы. Например, строка s = "key: 610" длиной 8 символов:
key: 610
     [  )
012345678


В ней подстрока 610 выкусывается диапазоном [5, 8), длина её 8 — 5 = 3 без ±1. Да, 8 здесь указывает на позицию вне массива, как и всегда в обращении array[length]. Проблем это не создаёт, потому что к элементу s[8] обращения нет, все индексы перебираются в асимметричных ограничениях 5 ≤ i < 8 без ±1.
Глаза у меня добрые, но рубашка — смирительная!
Re[8]: Ну тогда надо обшадить и всю скриптоту
От: Wolverrum Ниоткуда  
Дата: 06.12.19 01:35
Оценка: +1
Здравствуйте, Kolesiki, Вы писали: [-0]
Может вам и не очевидно, но народ в МС просто сделал по традиции всех (не могу даже вспомнить хоть один контрпример)
скриптовых ЯП

А в них, еще до рождения шипров, был стандарт: x[-1] — последний элемент списка/массива
Руби, Перл, "Наше автоматизация всё" Groovy(!), "наше ИИ всё" Python etc

А вы тут... развели категорическую бодягу.

Хотя нет, мир .NET упорно сопротивлялся этой весьма удобной фишке, в Powershell увы тоже нет.

Что не одобряю, так это испольвание уродской крыши вместо нормального, привычного отрицательного индекса
Отредактировано 06.12.2019 1:46 Wolverrum . Предыдущая версия .
Re[9]: Ну тогда надо обшадить и всю скриптоту
От: alexzzzz  
Дата: 06.12.19 07:29
Оценка:
W>Что не одобряю, так это испольвание уродской крыши вместо нормального, привычного отрицательного индекса

Проблема с минус нулём.

var start = 0;
var end = -0;
var slice = array[start..end];
Отредактировано 06.12.2019 8:37 alexzzzz . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.