Методы, которые что-то ищут, но могут не найти

6yrop

[C#]
Методы типа string.IndexOf (находит подстроку в строке int.TryParse (преобразовывает строку в целое) могут успешно найти и вернуть результат, но могут и ничего не найти, и тогда придумывают всякие условности возвращать "-1" или null.
На мой взгляд, запись через делегаты

IntEx.Parse(
"123",
i => Console.WriteLine("i = {0}", i
=> Console.WriteLine("conversion failed")
;


public static Action Parse(string arg,
Action<int> successAction, Action failedAction)
{
int result;
if (int.TryParse(arg, out result
{
return => successAction(result);
}
else
{
return failedAction;
}
}

более логична. В такой сигнатуре есть все что нужно, и нет ничего лишнего! Из сигнатуры метода явно видно, что есть ситуация, когда число может не распарситься. У метода string.IndexOf этого не видно, и надо самому вспомнить про это и лезть в доку, смотреть как эта ситуация обрабатывается. У метода int.TryParse ситуация немного получше, но out параметр тоже нельзя назвать простым решением, кстати, этот метод появился только во второй версии. Да и условный оператор опять таки усложняет понимание кода

int result;
if (int.TryParse("123", out result
{
Console.WriteLine("i = {0}", result);
}
else
{
Console.WriteLine("conversion failed");
}

Здесь переменная result может использоваться в обоих ветках if-а, но во второй ветки она не имеет смысла, и компилятор это не отследит. Иногда просто перепутаешь true/false, которые возвращает TryParse, а для теста, по крайней мере, надо запустить прогу, а это не всегда удобно в момент написания.
На таком примитивном примере выгода мизерная, но, имхо, если привыкнуть к такому стилю, то код будет в целом надежнее.

P.S. да еще надо про пефоманс помнить, но это там где он критичен

karkar

А можно чуть более реальный пример? Например, через TryParse и IndexOf можно решить такую задачу: есть строка, в которой идут целые числа, разделенные пробелами ("12 34 567 8" найти их сумму. Как это будет выглядеть при твоем подходе?

Dasar

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

int? result = int.TryParse(value);


if (result != null)
{
Console.WriteLine("i = {0}", result);
}
else
{
Console.WriteLine("conversion failed");
}


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

IntEx.Parse(
"123",
i => Console.WriteLine("i = {0}", i
=> Console.WriteLine("conversion failed")
;

Dmitriy82

Очень часто в особом случае требуется сделать break, continue или return, на что делегаты неспособны.
А вообще это чем-то напоминает continuation-passing style.

tokuchu

1) Вообще, ты ругаешь int.TryParse за то, что непонятно сразу кагда true, а когда false, за то что нужна структура с if. Но ведь в твоём предложении абсолютно то же самое - каким должен быть первый делегат - который при удаче или при неудаче. И структура кода от if-а не особо сильно отличается.
2) Вроде как для таких ситуаций, когда нужно возвращать и значение и ошибку, придуман механизм исключений.

6yrop

А можно чуть более реальный пример? Например, через TryParse и IndexOf можно решить такую задачу: есть строка, в которой идут целые числа, разделенные пробелами ("12 34 567 8" найти их сумму. Как это будет выглядеть при твоем подходе?
Вот так

public static Action Sum(string str,
Action<int> successAction, Action failedAction)
{
int sum = 0;
int toIndex = 0;
while (true)
{
string subString = default(string);
bool isContinueFind = default(bool);
Action beforeContinueFindAction = default(Action);
Util.IndexOf(
str, " ", toIndex,
index =>
{
subString = str.Substring(toIndex, index - toIndex);
isContinueFind = true;
beforeContinueFindAction = => toIndex = index + 1;
},
=>
{
subString = str.Substring(toIndex);
isContinueFind = false;
beforeContinueFindAction = => { throw new ApplicationException; };
};
bool isParsed = default(bool);
IntEx.Parse(
subString,
i =>
{
sum += i;
isParsed = true;
},
=>
{
isParsed = false;
};
if (isParsed == false)
{
return failedAction;
}
if (isContinueFind == false)
{
break;
}
else
{
beforeContinueFindAction;
}
}
return => successAction(sum);
}

См. продолжение в следующих постах.

6yrop

или return
return можно сделать, но для полноты придется задваивать методы. К сожалению, нет типа void :(
 

public class Util
{
public static Func<Tresult> IndexOf<Tresult>(string arg, string value, int startIndex,
Func<int, Tresult> successAction, Func<Tresult> failedAction)
{
int indexOf = arg.IndexOf(value, startIndex);
if (indexOf == -1)
{
return failedAction;
}
else
{
return => successAction(indexOf);
}
}

public static Action IndexOf(string arg, string value, int startIndex,
Action<int> successAction, Action failedAction)
{
var result = IndexOf<object>(
arg, value, startIndex,
i =>
{
successAction(i);
return null;
},
=>
{
failedAction;
return null;
}
);
return => result;
}
}

Замечу, что исходный метод string.IndexOf естественным образом выражается через Util.IndexOf
 

int indexOf = Util.IndexOf("aaa", "a", 0,
index => index, => -1;

при этом пользователь сам определяет, что возвращать, если символ не найден.

6yrop

Если пользоваться методами с return-ом, то подсчет суммы можно переписать вот так

public static Func<Tresult> Sum<Tresult>(string str,
Func<int, Tresult> successFunc, Func<Tresult> failedFunc)
{
int sum = 0;
int toIndex = 0;
while (true)
{
bool isContinueFind = default(bool);
Action beforeContinueFindAction = default(Action);
if (IntEx.Parse(
Util.IndexOf(
str, " ", toIndex,
index =>
{
isContinueFind = true;
beforeContinueFindAction = => toIndex = index + 1;
return str.Substring(toIndex, index - toIndex);
},
=>
{
isContinueFind = false;
beforeContinueFindAction = => { throw new ApplicationException; };
return str.Substring(toIndex);
}
i =>
{
sum += i;
return true;
},
=> false

== false)
{
return failedFunc;
}
if (isContinueFind == false)
{
break;
}
else
{
beforeContinueFindAction;
}
}
return => successFunc(sum);
}

6yrop

или даже так :grin: :grin: :grin:
 

public static Func<TResult> Sum<TResult>(string str,
Func<int, TResult> successFunc, Func<TResult> failedFunc)
{
int sum = 0;
int toIndex = 0;
while (true)
{
var findController = Util.IndexOf<IFindController>(
str, " ", toIndex,
index => new ContinueFindController(
str.Substring(toIndex, index - toIndex
=> toIndex = index + 1
=> new BreakFindController(str.Substring(toIndex
;
if (IntEx.Parse(
findController.FoundString,
i =>
{
sum += i;
return true;
},
=> false

== false)
{
return failedFunc;
}
if (findController.IsContinue == false)
{
break;
}
else
{
findController.BeforeContinue;
}
}
return => successFunc(sum);
}

private interface IFindController
{
string FoundString { get; }
bool IsContinue { get; }
void BeforeContinue;
}

private class ContinueFindController : IFindController
{
private readonly string foundString;
private readonly Action beforeContinueFindAction;

public ContinueFindController(string foundString, Action beforeContinueFindAction)
{
this.foundString = foundString;
this.beforeContinueFindAction = beforeContinueFindAction;
}

public string FoundString
{
get { return foundString; }
}

public bool IsContinue
{
get { return true; }
}

public void BeforeContinue
{
beforeContinueFindAction;
}
}

private class BreakFindController : IFindController
{
private readonly string foundString;

public BreakFindController(string foundString)
{
this.foundString = foundString;
}

public string FoundString
{
get { return foundString; }
}

public bool IsContinue
{
get { return false; }
}

public void BeforeContinue
{
throw new ApplicationException;
}
}

P.S. пока это эксперимент, сильно не бейте :grin:

6yrop

на остальные посты отвечу чуть позже

karkar

Спасибо! Получилось как я ожидал - смесь функциональщины с императивщиной с активной передачей состояния через переменные (сайд эффекты).
А с оригинальными функциями это может выглядеть так:

static int Sum(string str)
{
int i, sum_rest = 0, first_num;
if i = str.IndexOf(' ' >= 0)
{
sum_rest = Sum(str.Substring(i + 1;
str = str.Substring(0, i);
}
if (int.TryParse(str, out first_num
return first_num + sum_rest;
else
throw new ApplicationException("can't convert to number");
}

Теперь сравни сложность обоих подходов и вероятность внесения ошибки. Действительно ли есть какой-то выигрыш?

Dasar

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

static int Sum(string str)
{
try
{
return str.Split(' ').Sum(value => int.Parse(value;
}
catch(FormatException exc)
{
throw new ApplicationException("can't convert to number", exc);
}
}

а это если нужно проигнорировать невалидные числа

static int Sum(string str)
{
return str.Split(' ').Sum(value => Util.Parse(value ? 0;
}

class Util
{
static public int? Parse(string value)
{
int result;
if (int.TryParse(value, out result
return result;
return null;
}
}

6yrop

постоянные пробеги по строке
это условие задачи
через TryParse и IndexOf можно решить такую задачу

Dasar

тогда так, а сама Sum-а такая же как в предыдущем коде

IEnumerable<string> Split(string str)
{
var index = 0;
for (;;)
{
var pos = str.IndexOf(index, ' ');
if (pos == -1)
break;
yield return str.Substring(index, pos-index-1);
index = pos+1;
}
if (index < str.Length)
yield return str.Substring(index);
}

Dasar

в первую очередь мне не нравится твой код тем, что не понятно как его модифицировать дальше.
допустим у нас по ходу написания программы возникла задача отладки: необходимо вывести на консоль в строку через запятую части, на которые мы побили строку через indexOf.
как ты эту задачу будешь решать?
в коде с атомарными операциями все просто: код отладки просто вставляется между кодом разбора и суммирования

int Sum(string str)
{
var values = str.Split;
Console.WriteLine(values.JoinToString(", ";
return values.Sum(value => int.Parse(value;
}

bleyman

Довольно мило, но во-первых чота ты увлекаешься, ну, реально стрёмный лишний непонятный код получается.
А во-вторых, нафига, прости, ты откладываешь вызов коллбэка? Я понимаю, что это очень крутая фишка, но надо всё-таки на пользу смотреть, а не на количество и крутизну использованных фишек. Откладывая вызов ты никогда не сделаешь ничего полезного, я гарантирую это, зато можешь сделать что-нибудь неожиданное и вредное.

6yrop

Получилось как я ожидал - смесь функциональщины с императивщиной с активной передачей состояния через переменные (сайд эффекты).
А с оригинальными функциями это может выглядеть так:
Теперь сравни сложность обоих подходов и вероятность внесения ошибки. Действительно ли есть какой-то выигрыш?
Это просто другой алгоритм, давай сравнивать записи на языке C# одного алгоритма. Вот как твой алгоритм выглядит в моей записи.

static int Sum2(string str)
{
int sum_rest = 0;
Util.IndexOf(
str, " ", 0,
index =>
{
sum_rest = Sum2(str.Substring(index + 1;
str = str.Substring(0, index);
},
=> { }
;
return IntEx.Parse(
str,
i => i + sum_rest,
=>
{
throw new ApplicationException("can't convert to number");
}
;
}

6yrop

в первую очередь мне не нравится твой код тем, что не понятно как его модифицировать дальше.
допустим у нас по ходу написания программы возникла задача отладки: необходимо вывести на консоль в строку через запятую части, на которые мы побили строку через indexOf.
как ты эту задачу будешь решать?
в коде с атомарными операциями все просто: код отладки просто вставляется между кодом разбора и суммирования
code:
int Sum(string str)
{
var values = str.Split;
Console.WriteLine(values.JoinToString(", ";
return values.Sum(value => int.Parse(value;
}
у тебя в последнем варианте метод Split возвращает IEnumerable<string>, у этого типа нет метода JoinToString :smirk:
В моем алгоритме распечатать все не всегда возможно, поскольку поиск чисел и суммирование идет в один проход.
Можно печатать найденные числа (если заморачиваться на то, чтобы не было лишней запятой, то как-то так)

private class Writer
{
private bool start = true;
public void Write(string s)
{
if (start == false)
{
Console.Write(", ");
}
Console.Write(s);
start = false;
}

public void EndWrite
{
Console.WriteLine;
}
}

public static Func<TResult> Sum<TResult>(string str,
Func<int, TResult> successFunc, Func<TResult> failedFunc)
{
int sum = 0;
int toIndex = 0;
var writer = new Writer;
while (true)
{
var findController = Util.IndexOf<IFindController>(
str, " ", toIndex,
index => new ContinueFindController(
str.Substring(toIndex, index - toIndex
=> toIndex = index + 1
=> new BreakFindController(str.Substring(toIndex
;
writer.Write(findController.FoundString);
if (IntEx.Parse(
findController.FoundString,
i =>
{
sum += i;
return true;
},
=> false

== false)
{
return failedFunc;
}
if (findController.IsContinue == false)
{
break;
}
else
{
findController.BeforeContinue;
}
}
writer.EndWrite;
return => successFunc(sum);
}

Dasar

у тебя в последнем варианте метод Split возвращает IEnumerable<string>, у этого типа нет метода JoinToString
да, нет такого метода
зато есть такой код дополнительно

public static class StringHlp
{
public static string JoinToString(this IEnumerable<string> items, string separator)
{
return string.Join(separator, items.ToArray;
}
}

6yrop

Замечу, что исходный метод string.IndexOf естественным образом выражается через Util.IndexOf
 
code:
     int indexOf = Util.IndexOf("aaa", "a", 0,
     index => index, => -1;
  
при этом пользователь сам определяет, что возвращать, если символ не найден.
я тут был не до конца прав, в случае TryParse эквивалентной записи нет. Такая запись
 

int result = default(int);
IntEx.Parse(
"123",
i =>
{
result = i;
return true;
},
=>
{
result = default(int);
return false;
};

хуже, чем out параметром, приходится инициализировать переменную, да и переменных получается два i, result. Но такая ситуация возникает только, если надо использовать break или continue (см. пост выше). Причем есть возможность написать боле безопасный код, чем с out параметром (хотя и более громоздкий код получается)
 

var parseResult = IntEx.Parse<IParseResult>(
"123",
i => new SuccessParseResult(i
=> new FailedParseResult
;

private interface IParseResult
{
bool IsParsed { get; }
int Value { get; }
}

private class SuccessParseResult: IParseResult
{
private readonly int value;

public SuccessParseResult(int value)
{
this.value = value;
}

public bool IsParsed
{
get { return true; }
}

public int Value
{
get { return value; }
}
}

private class FailedParseResult : IParseResult
{
public bool IsParsed
{
get { return false; }
}

public int Value
{
get
{
throw new ApplicationException;
}
}
}

Здесь мы хотя бы в рантайме кидаем исключение (при чтении кода тоже видно, что в эту ветку исполнение не должно заходить в случае с out параметром переменную можно использовать даже, когда метод вернул false.

6yrop

зато есть такой код дополнительно
ух, не привык я еще к расширенным методам :).
Но суть дискуссии — сравнить записи одного и того же алгоритма. Твой алгоритм тоже записывается с помощью моего подхода.

6yrop

прыгающее спагети
это что-то новое :grin: , обычно спагетти называют как раз код, который тянется в линию, разветвляется и запутывается, как спагетти. Как раз там передают данные типа флажков, null-ов и "-1".

6yrop

 
int? result = int.TryParse(value);
if (result != null)
{
    Console.WriteLine("i = {0}", result);
}
else
{
    Console.WriteLine("conversion failed");
}
--------------------------------------------------------------------------------
здесь все четко - есть атомарная конструкция разбора числа, есть атомарная конструкция вывода значения
  

я бы не сказал, что тут все четко. Разбираем число и получаем null, совсем не очевидно, что его следует интерпретировать, как не удачный разбор.
 
IntEx.Parse(
    "123",
    i => Console.WriteLine("i = {0}", i
     => Console.WriteLine("conversion failed")
    ;
  

А вот тут как раз все четко. Метод принимает два "куска кода", выбирает один из них, и возвращает в качестве результата. Ни каких специальных договоренностей не нужно.

6yrop

P.S. пока это эксперимент, сильно не бейте
если забить на потери пефоманса из-за создания Controller-ов, мне этот вариант начинает нравиться :)

Helga87

Чем код на 142 строчки лучше -евского:

int Sum(string str)
{
var values = str.Split;
return values.Sum(value => int.Parse(value;
}

Варианты ответа:
1. Читаемостью
2. Удобством отладки
3. Свой вариант

Fmouse

зато он внушает

Helga87

не знаю что остальным, а мне он внушает отвращение. Зачем простую задачу решать сложно?

Marinavo_0507

> Чем твой код на 142 строчки лучше -евского
А последний разве работает? :confused:

Helga87

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

6yrop

Чем твой код на 142 строчки лучше -евского:
code:
int Sum(string str)
{
var values = str.Split;
return values.Sum(value => int.Parse(value;
}
По ссылке всего 98 строк, я чего-то не понял?
Это разные алгоритмы, -й накапливает сабстрочки, я все делаю в один проход. В данном простом случае, наверное алгоритм -я предпочтительнее. Но суть то в том что бы сравнивать ОДИН алгоритм и разные ЗАПИСИ. Переписать -я через мой подход?

Dasar

это что-то новое , обычно спагетти называют как раз код, который тянется в линию, разветвляется и запутывается, как спагетти. Как раз там передают данные типа флажков, null-ов и "-1".
спагетти - в первую очередь пошло с goto
и как раз код с большим кол-вом колбаков и начинает походить на код с goto

Helga87

По ссылке всего 98 строк, я чего-то не понял?
Мб обсчитался. Кода много, в глазах рябит.
Переписать -я через мой подход?
А смысл? Чтобы было непонятно?
Это разные алгоритмы, -й накапливает сабстрочки, я все делаю в один проход.
Возможно, но твой код недостаточно ясен, чтобы с одного взгляда определить, что он делает.

6yrop

я все делаю в один проход
задача была как раз продемонстрировать подход на комбинации двух функций TryParse и IndexOf, а не реализовывать эту простую задачу оптимальным способом по читаемости или еще по чему.

Helga87

Пример продемонстрировал, что TryParse и IndexOf сочетать можно, но получается громоздко. Какие выводы надо сделать?
Вообще, я пока не понимаю смысл происходящего: тобой пишется какой-то код без хорошей аргументации, почему он лучше. Хорошая аргументация, например, — почему надо писать, как ты предлагаешь, что и почему станет лучше.

Marinavo_0507

А откуда такой странный values.Sum? Это в стандартной библиотеке такое?

6yrop

А смысл? Чтобы было непонятно?
нет :grin: , все останется так же понятно :) . Но код будет немного безопаснее, будет лучше рефакториться. Причем как формальный рефакторинг, так и с участием человеческого мозга :grin:

Dasar

А последний разве работает?
а что ему не работать?
если исправить пару мелких неточностей, то и компилируется, и суммирует

static IEnumerable<string> Split(string str)
{
var index = 0;
for (; ; )
{
var pos = str.IndexOf(' ', index);
if (pos == -1)
break;
yield return str.Substring(index, pos - index);
index = pos + 1;
}
if (index < str.Length)
yield return str.Substring(index);
}

static int Sum(string str)
{
return Split(str).Sum(value => Util.Parse(value ? 0;
}

class Util
{
static public int? Parse(string value)
{
int result;
if (int.TryParse(value, out result
return result;
return null;
}
}

Helga87

в .NET Framework 3.5 входит Linq, который позволяет довольно удобно работать с коллекциями объектов: делать выборки, агрегировать и пр. В частности, есть и агрегатор Sum.
Если бы его не было, можно было бы сделать соответствующий Extension метод. У того же написана достаточно удобная библиотека хелперов, которые позволяют писать код проще.

Helga87

все останется так же понятно
твой код в этом треде по большей части ни фига непонятный

6yrop

Хорошая аргументация, например, — почему надо писать, как ты предлагаешь, что и почему станет лучше.
основной аргумент — результат поиска возвращается в качестве параметра лямбды, и это значение всегда "хорошее", а там где оно не имеет смысла, там его нет.

6yrop

твой код в этом треде по большей части ни фига непонятный
ок, давай по шагам, вот есть две записи одного алгоритма
Чем второй менее понятный?

Marinavo_0507

Жесть. А они до конца не пошли, в том плане чтобы записи a.f(b) и f(a, b) были эквивалентны?

Helga87

Оба жрут O(n^2) памяти. Или я уже туплю под вечер?

6yrop

Пример продемонстрировал, что TryParse и IndexOf сочетать можно, но получается громоздко. Какие выводы надо сделать?
тк нас интересует не конкретно TryParse и IndexOf, а стиль с делегатами

6yrop

Оба жрут O(n^2) памяти. Или я уже туплю под вечер?
.......... :o :o :grin: , но эта характеристика сейчас не рассматривается

Helga87

Жесть. А они до конца не пошли, в том плане чтобы записи a.f(b) и f(a, b) были эквивалентны?
Пока нет.
Лично мне не нравится, что происходит с C#. Слишком легко стало писать плохо. В долговременной перспективе это означает эффект C++/PHP про который еще Линус говорил в ответ на вопрос "Почему ядро линукса на C, а не на С++"

Helga87

но эта характеристика сейчас не рассматривается
с учетом этой характеристики, такой код нельзя применять. Ведь, как мне поведал , каждый раз, когда мы пишем алгоритм со сложностью O(n^2 Бог убивает котенка.

6yrop

ну тк через несколько лет C# выкинут и сделают новый чистый язык

Helga87

тк нас интересует не конкретно TryParse и IndexOf, а стиль с делегатами
иногда делегаты удобны. Например, в коде они тоже используются.

Marinavo_0507

А сколько вы примерно времени тратите на изучение фишек новых версий языков, библиотек и т.п. - как тех, что нужно для текущих проектов, так и на перспективу, чтоб типа ценность на рынке труда не падала? Имхо это довольно скучно, ведь что-то концептуально новое бывает крайне редко.

slonishka

Видимо, Б-га нет, потому что такого кода дофига и котят тоже немало. :\

6yrop

Имхо это довольно скучно, ведь что-то концептуально новое бывает крайне редко
Согласен.
А в какой области часто возникает концептуально новое?

Helga87

А сколько вы примерно времени тратите на изучение фишек новых версий языков, библиотек и т.п. - как тех, что нужно для текущих проектов, так и на перспективу, чтоб типа ценность на рынке труда не падала? Имхо это довольно скучно, ведь что-то концептуально новое бывает крайне редко.
Могу ответить за себя: на данном этапе моей жизни на повышение своих навыков трачу порядка 15-20 часов в неделю + 40 часов работа, на которой то, что оказалось полезным, применяю.
Да, и надо отметить, что на C#/.NET жизнь не заканчивается. За последние полгода мне пришлось писать код примерно на 5 языках — C#, Java, Python, JavaScript, Sawzall.
Насчет же интереса — у меня он в основном проявляется в том, как бы сделать очередную задачу просто. Стараюсь бороться с детскими болезнями, когда для простых вещей я писал свой язык+интерпретатор или же декомпилятор+анализатор.

6yrop

15-20 часов в неделю + 40 часов работа, на которой то, что оказалось полезным, применяю.
ты как-то так отделил, ты на работе не читаешь что ли?

Helga87

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

bleyman

Эмм.
Ты действительно сейчас переизобрёл continuation-passing style.
Я понимаю, что оно выглядит прикольно и позволяет развлечь себя нетривиальным кодингом, напрячь моск и всё такое.
Но. Скачай Paul Graham's "On Lisp", и посмотри на страницу 273, где он показывает, как простая функция для реверсирования списка выглядит после CPS transform. Внимательно посмотри, попытайся понять, как оно работает. Я больше пятнадцати минут ффтыкал в эти восемь строчек, прежде чем вроде бы как понял, но забыл тут же как только потерял концентрацию, прям как с Y-combinator.
Это — логическое завершение того, что тебя сейчас так развлекает.
Подумай о том, что непопулярность лиспа довольно сильно определяется тем, что некоторые его конструкции тяжелы для понимания. Подумай о том, что даже убеждённый лиспофаг Грэхэм пишет: "A Lisp program can be transformed into a form called “continuation-passing style.” Programs which have undergone complete CPS conversion are impossible to read <..>".
Забей, короче. Этот путь не принесёт тебе ничего хорошего.

6yrop

Мб обсчитался. Кода много, в глазах рябит.
Возможно, но твой код недостаточно ясен, чтобы с одного взгляда определить, что он делает.
Если переходить на флуд, то есть вот такое мнение
 
“Один из наиболее трудных для понимания моментов в объектно-ориентированной программе связан с общим потоком управления. При хорошем проектировании в различных классах определяется множество небольших методов, и временами понять последовательность поведения системы бывает довольно сложно. Можно достаточно долго вглядываться в код, пытаясь понять, что же делается в программе. Особенную трудность это вызывает у тех, кто впервые сталкивается с объектным подходом. Диаграммы последовательности как раз и помогают разобраться в процессе поведения системы.”
М. Фаулер, “UML в кратком изложении”
  

И при всем при этом Фаулер проповедует использование объектно-ориентированного программирования. То что с первого взгляда код не понятный, это еще ни о чем не говорит. Да и смотреть надо под ReShaper-ом :D

Marinavo_0507

Ну вот например в сетевых технологиях вроде бы нет такого, что все фичи давно придуманы и опробованы, только не реализованы крупными вендорами.
Ухитряются что-то новое и придумывать и реализовывать.

6yrop

Вот так еще проще, короче и понятнее, без всяких yield return, лямбд и встроенных сумматоров.

static int Sum(string str)
{
var index = 0;
var sum = 0;
for (; ; )
{
var pos = str.IndexOf(' ', index);
var subString = pos == -1 ? str.Substring(index) : str.Substring(index, pos - index);
int parseResult;
if (int.TryParse(subString, out parseResult
sum += parseResult;
if (pos == -1)
break;
index = pos + 1;
}
return sum;
}

rosali

> всякие условности возвращать "-1" или null.
по моему с тех пор как в .NET появилось значение null у структур, эта проблема исчезла.
а если null-а не хватает чтобы выразить "вторую" ветку, то вместо передачи функций параметрами удобнее программировать на case-ах. В .NET-е сейчас я так понимаю ничего похожего нет, ну появится когда нибудь. Я имею ввиду хаскельное
data TryParse = Result Int | ErrorPosition Int
tryParse :: String -> TryParse
case tryParse x of
Result r => ...
ErrorPosition e => ...
вместо мутного
tryParse :: String -> (Int -> a) -> (Int -> a) -> a

6yrop

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

6yrop

Ну вот например в сетевых технологиях вроде бы нет такого, что все фичи давно придуманы и опробованы, только не реализованы крупными вендорами.
Ухитряются что-то новое и придумывать и реализовывать.
можно несколько примеров концептуально нового, что было реализовано в последние 5 лет, чего до этого не было описано в научных статьях?

Dasar

Вот так еще проще, короче и понятнее, без всяких yield return, лямбд и встроенных сумматоров.
так мне меньше нравится, т.к. надо вглядываться что именно делается.
надо смотреть не забыли ли учесть последний элемент, или первый элемент.
с Split и Sum сразу видно, что именно делается.

6yrop

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

if (index < str.Length)

Почему только правый? почему только концевой?

Dasar

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

var subString = pos == -1 ? str.Substring(index) : str.Substring(index, pos - index);

6yrop

нет, не тоже самое, упомянутое условие у тебя можно опустить
 

var index = 0;
for (; ; )
{
var pos = str.IndexOf(' ', index);
if (pos == -1)
{
yield return str.Substring(index);
break;
}
yield return str.Substring(index, pos - index);
index = pos + 1;
}

Marinavo_0507

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

Dasar

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

6yrop

кстати у тебя не обрабатывается ситуация, когда в конце строки есть лишний концевой пробел.
конечный результат полностью совпадает с твоим. У тебя почему-то пробел в конце строки отрезается на этапе Split-а. А у меня все отрезается в одном месте — на этапе TryParse-а.
Если уловие задачи изменится, например, для всех невалидных строчек надо кидать исключение:
для такой "1 2 3 "
и для таких
 " 1 2 3", "1         2 3"  

то у тебя придется менять два метода.

Dasar

А сколько вы примерно времени тратите на изучение фишек новых версий языков, библиотек и т.п. - как тех, что нужно для текущих проектов, так и на перспективу, чтоб типа ценность на рынке труда не падала? Имхо это довольно скучно, ведь что-то концептуально новое бывает крайне редко.
на новые фишки мало времени уходит
много времени уходит на изучение технологий

Marinavo_0507

> много времени уходит на изучение технологий
что такое "технология", как не ещё одна реализация известной идеи?

Dasar

то у тебя придется менять два метода.
менятся будет только split

Dasar

что такое "технология", как не ещё одна реализация известной идеи?
и реализацией какой идеи является технология Linux?
или технология html? или технология Flash? или технология Avi?

6yrop

менятся будет только split
напиши вариант, когда кидается исключения для строчек

"1 2 3"
"1qwe2 3"

Dasar

"1         2 3" 

это должно быть 1, 2, 3 или 1, ошибка, ошибка, 2. 3?

6yrop

это должно быть 1, 2, 3 или 1, ошибка, ошибка, 2. 3?
просто исключение new ApplicationException("can't conver to number")

Marinavo_0507

> и реализацией какой идеи является технология Linux?
не знаю такой технологии
Linux - ещё одно юникс-подобное ядро
> или технология html? или технология Flash? или технология Avi?
ещё один markup, ещё одна виртуальная машина со встроенными графическими примитивами , ещё один контейнер

6yrop

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

Dasar

просто исключение new ApplicationException("can't conver to number")
это уже было


static int Sum(string str)
{
try
{
return str.Split(' ').Sum(value => int.Parse(value;
}
catch(FormatException exc)
{
throw new ApplicationException("can't convert to number", exc);
}
}

6yrop

с твоей реализацией Split доя строки "1 2 3 " исключения не будет

Helga87

если бы я пользовался твоей библиотекой, в которой был бы метод Sum, я бы достаточно сильно удивился, увидев, что она падает на строке "1 2 3 ".

6yrop

а на " 1 2 3" она должна падать?

Marinavo_0507

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

6yrop

Кстати, майкросовт и сан далеко не все реализовало, а пользоваться чем-то сторонним не всегда удобно. Вот, например, я так и не нашел реализации — чтобы для расчетов можно было просто писать формулкки, не заморачиваясь, когда они пересчитываются, и пусть сами обновляются на экране (Dataflow
http://en.wikipedia.org/wiki/Dataflow
)

Dasar

с твоей реализацией Split доя строки "1 2 3 " исключения не будет
со стандартной реализацией split-а будет, или если мою реализацию довести до стандартной, убрав последний if

Marinavo_0507

А ёксель разве так не делает?

Helga87

а на " 1 2 3" она должна падать?
на мой взгляд, она должна принимать на вход "(\s*[-]?\d+)+\s*". Метод ДаркГрея поменяется слабо.

private static Regex regex = new Regex(@"(\s*[-]?(?<n>\d++\s*", RegexOptions.Compiled);

int Sum(string str)
{
var values = regex.Matches(str).Groups["n"];
return values.Sum(mc => int.Parse(mc.Value;
}

А как поменяется твой мегакод?
UPD: вторая версия кода, т.к. в предыдущий раз чуть-чуть налажал с regex-ом.
private static Regex validator = new Regex(@"(\s*([-]?\d++\s*", RegexOptions.Compiled);
private static Regex regex = new Regex(@"[-]?\d+", RegexOptions.Compiled);

private static int Sum(string str)
{
if (!validator.IsMatch(str
{
throw new ApplicationException("wrong input line");
}
var values = regex.Matches(str);

return values.OfType<Match>.Sum(mc => int.Parse(mc.Value;
}

6yrop

А ёксель разве так не делает?
да эксель это уже давно делает. Представь непонимание людей (они каждый день работают с экселям когда они заказывают программное обеспечение и им говорят, что оно будет реализовано на самой современной платформе, а потом выясняется, что простые (но их много) вычисления реализовано с большим количеством недочетов, а добавление новых требует не мало времени.

Marinavo_0507

ну а как же там хвалёный activex или как там сейчас это называется (опять забыл)?

Dasar

ещё один markup, ещё одна виртуальная машина со встроенными графическими примитивами , ещё один контейнер
так можно еще проще все объяснять: еще один адаптер одной хрени в другую.
это же только в идеальном вымышленном мире: например, в мире математики, или в мире машины тьюринга(машины лиспа) - все очень просто, и можно обойтись одними идеями.
а технология - это в первую очередь компромисс между несколькими идеями, компромисс между идеями и ограничениями реальной жизни и т.д.
самое главное - что идеи не позволяют ничего сделать, они позволяют только что-то объяснить, но не сделать.
соответственно зная идею, но не имея или не зная технологий эту идею реализующих - можно быть крутым теоретиком, но не практиком, не человеком, который реально что-то делает и меняет мир.

6yrop

ну а как же там хвалёный activex или как там сейчас это называется, опять забыл?
ты это к тому, чтобы эксель встроить в приложение?

Marinavo_0507

ну да

6yrop

А как поменяется твой мегакод?
примерно также

6yrop

ну во-первых эксель должен стоять, а если это веб-решение, и за лицензию платить надо.
Во-вторых, гонять данные в эксель и обратно эта задача, наверное, даже сложнее, чем сами вычисления :). Т.е. у нас данные в объектах или в реляционной базе, а в какой форме их переливать в эксель не совсем ясно.

6yrop

кстати этот код работает?

Helga87

примерно также
показать сможешь?

Helga87

кстати этот код работает?
2-я версия - да. Только что проверял.

6yrop

private static Regex validator = new Regex(@"(\s*([-]?\d++\s*", RegexOptions.Compiled);
private static Regex regex = new Regex(@"([-]?\d+)", RegexOptions.Compiled);
регэкспы вроде как похожи. Одним регэкспом нельзя обойтись?

Helga87

Можно. Но у меня код обработки усложнился от этого.

Marinavo_0507

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

Helga87

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

6yrop

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

Marinavo_0507

Согласен, но по-моему невесело как-то этим прикладным программистам приходится.

Helga87

стандартная ситуация, когда один код отвечает за валидацию, другой - за обработку данных. Такое разделение упрощает логику и не имеет заметных недостатков. Самый яркий пример: xml, который валидируется по схеме, а затем обрабатывается совсем другими средствами, которые имеют гарантию, что входные данные корректны, а значит, можно все делать проще.

6yrop

показать сможешь?
могу конечно, но я думаю ты понимаешь, что многое там исчезнет, поскольку было завязано на использование IndexOf (условие задачи). Теперь мы ищем подстроки регэкспом.
Но у -я тоже бОльшая часть исчезает.

Dasar

на мой взгляд, она должна принимать на вход "(\s*[-]?\d+)+\s*". Метод ДаркГрея поменяется слабо.
имхо, с regex-ом сложно
вроде все на split-е можно сделать
упасть на любой ошибке
str.Split(' ').Sum(value => int.Parse(value
проигнорировать все ошибки
str.Split(' ').Sum(value => IntHlp.TryParse(value ? 0
проигнорировать все лишние разделители, но упасть на ошибках
str.Split(' ')
.Where(value => !value.IsNullOrEmpty
.Sum(value => int.Parse(value
проигнорировать лишние концевые разделители, но упасть на остальных ошибках
str.Split(' ')
.Trim(value => value.IsNullOrEmpty
.Sum(value => int.Parse(value

Marinavo_0507

> Такое разделение упрощает логику и не имеет заметных недостатков.
недостаток - неинформативность сообщений об ошибках
в твоём случае "неверная строка", и сиди ищи что там не так
решение лучше в этом смысле, там понятно, на в каком месте ошибка
а, ну ещё недостаток - дублирование части логики разбора
могут рассинхронизоваться эти два регекспа

Helga87

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

6yrop

разделение упрощает логику и не имеет заметных недостатков.
Если придется отрабатывать числа с десятичной точкой, оба регэкспа изменятся, так? это не является недостатком?

Helga87

> Такое разделение упрощает логику и не имеет заметных недостатков.
недостаток - неинформативность сообщений об ошибках
в твоём случае "неверная строка", и сиди ищи что там не так
old-skool решение лучше в этом смысле, там понятно, на в каком месте ошибка
это недостаток не разделения, а конкретно моего кода. Т.к. валидатор вполне может указывать, в каком месте у него ошибка. Более того, обычно так и делают.

Marinavo_0507

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

Helga87

Если придется отрабатывать числа с десятичной точкой, оба регэкспа изменятся, так? это не является недостатком?
если меняется формат входных данных, то меняется код валидации. Вполне ожидаемо и не является проблемой — изменится совсем немного.
Ты уже написал свой код, который умеет такое парсить? Или уже слишком геморно это делать твоим методом?

Helga87

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

Marinavo_0507

ну вот движущийся робот не за счёт переписывания кода с java на c# развивается, а наверное за счёт новых алгоритмов распознавания образов, принятия решений и т.п.

Marinavo_0507

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

Helga87

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

Helga87

то есть нету ни у микрософта, ни у сана с айбиэм фреймворков для роботов вроде, люди начинают с идей и заканчивают прототипом
Microsoft Robotics Studio
Lego Mindstorms
сразу есть стандартные модули (типа — камера, инфракрасная камера, сустав, моторчик и пр. среда разработки, отладки, эмулятор.

6yrop

Ты уже написал свой код, который умеет такое парсить? Или уже слишком геморно это делать твоим методом?
Если смотреть твой код, то там уже нет ни IndexOf ни TryParse. Мой метод отличался только в способе использования этих функций. Теперь их нет, а значит и "моего" метода нет, код полностью совпадет с твоим.

Helga87

т.е. если изначально ты понял задачу распознать "\d+([ ]\d)*", а затем условия уточнили и стало возможным как угодно добавлять пробелы, тебе придется переписать все с нуля? Заменив свой код на ДаркГреевский?

Dasar

Ну вот мне кажется, что таким гораздо меньше нужно ботать способы сделать то же самое, но с другим синтаксисом и с новыми граблями.
замечал, что многие зрелые проекты спотыкаются в развитии, т.к. развитие начинает сдерживаться недостатками в используемых технологиях.
например, C/C++ - ошибками с памятью,
динамические языки - из-за сложностью с мелким рефакторингом (например, метод переименовать
статические языки - из-за сложностью с крупным рефакторингом и из-за сложностью "с попробовать" (сложно реализовывать только часть)
соответственно, использование более новых технологий даже с новыми граблями позволяют этот барьер сдвинуть дальше.

6yrop

Заменив свой код на ДаркГреевский?
ты можешь запостить что общего и чем различается твой код и код ДакГрея?

Helga87


int Sum(string str)
{
var values = str.Split;
return values.Sum(value => int.Parse(value;
}

:
private static Regex validator = new Regex(@"(\s*([-]?\d++\s*", RegexOptions.Compiled);
private static Regex regex = new Regex(@"[-]?\d+", RegexOptions.Compiled);

private static int Sum(string str)
{
if (!validator.IsMatch(str
{
throw new ApplicationException("wrong input line");
}
var values = regex.Matches(str);

return values.OfType<Match>.Sum(mc => int.Parse(mc.Value;
}

Основная логика та же, добавлено 6 строчек, изменено две. А у тебя?

Marinavo_0507

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

Helga87

по первому впечатлению, это далеко от переднего края
то есть такие штуки, чтоб непрофессионал мог поиграться с тем, что профессионалы делают уже давно
разница в том, что в области робототехники этот передний край вроде как есть, а в "бухгалтерии", откуда растут примеры этого треда - скорее нет
ты серьезно или смеешься? Если небольшая группа, скажем 2-3 человека заходят сделать ту же змею, они чо, должны сами делать радиосвязь, моторчики, камеры? Или все-таки они возьмут по-максимуму стандартные части, а сделают только то, что отличается?

Dasar

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

6yrop

код ДакГрея вот

static IEnumerable<string> Split(string str)
{
var index = 0;
for (; ; )
{
var pos = str.IndexOf(' ', index);
if (pos == -1)
break;
yield return str.Substring(index, pos - index);
index = pos + 1;
}
if (index < str.Length)
yield return str.Substring(index);
}

static int Sum(string str)
{
return Split(str).Sum(value => Util.Parse(value ? 0;
}

class Util
{
static public int? Parse(string value)
{
int result;
if (int.TryParse(value, out result
return result;
return null;
}
}

С кодом у него общее только

Sum

Marinavo_0507

ну а как эта группа мир-то изменит?
сделали змею, респект получили, всё

Helga87

по первому впечатлению, это далеко от переднего края
то есть такие штуки, чтоб непрофессионал мог поиграться с тем, что профессионалы делают уже давно
разница в том, что в области робототехники этот передний край вроде как есть, а в "бухгалтерии", откуда растут примеры этого треда - скорее нет
и вот есть отличный пример кафедры оптимального управления на ВМиК. Там есть довольно много дедов, которые стоит "на переднем крае". Ну, они так думают. И поскольку они профессионалы, используют только самодельные средства.
В результате люди у них по три года тратят на то, чтобы сделать тележку, которая едет на свет. Из полезного — люди у них хоть паять учатся.
Случай реальный, коллега, который учился на этой кафедре, рассказывал о своем студенчестве/аспирантуре.

Helga87

ну а как эта группа мир-то изменит?
сделали змею, респект получили, всё
там на голове камера. Теперь японские военные/спецслужбы могут заглядывать за угол или даже в другое помещение. Всунут змеюке в рот патрон — совсем зверюга станет.

Marinavo_0507

ну МГУ я думаю не стоит приводить в пример - сильно гнилое заведение
а деды эти были когда-то на переднем крае, и я думаю, им было сильно веселее, чем тем, кто змею сделал

Helga87

код ДакГрея вот
а не код ДаркГрея? Не юли, плз.

Helga87

ну МГУ я думаю не стоит приводить в пример - сильно гнилое заведение
а деды эти были когда-то на переднем крае, и я думаю, им было сильно веселее, чем тем, кто змею сделал
еще веселее моему соседу по подъезду. Но у него справка из психушки.
МГУ гнилое заведение, потому что аргументы "зачем использовать новое и стандартное, если у нас есть самодельное старье" применяются везде.

Helga87

там на голове камера. Теперь японские военные/спецслужбы могут заглядывать за угол или даже в другое помещение. Всунут змеюке в рот патрон — совсем зверюга станет.
включаем фантазию.
Ночь. В окопе сидят бойцы и напряженно смотрят вперед, в сторону расположения врага — пойманный язык сообщил, что в пять утра начнется атака.
5:02 — никого нет.
5:03 — приползли 50 змеюк, каждая выстрелила по разу, больше никого в окопе нет. Только тележка с надписью МГУ ползет на свет.
5:04 — атака закончена, высота взята.

6yrop

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

Как раз ты выдираешь отдельные посты из треда, и делаешь какие-то выводы. А то с чего начинался тред не хочешь замечать.

Marinavo_0507

это всё круто, но ведь пчолы с нанотехнологиями под брюхом прилетят быстрее

Helga87

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

6yrop

Окончательная работающая версия кода ДакГрея та, которую я привел.

Helga87

Окончательная работающая версия кода ДакГрея та, которую я привел.
Еще раз — Даркгреевский код, который я привел, если стереть отладочную печать, компилируется и работает. Этот код решает задачу суммирования чисел, разделенных пробелом.
Остальной код, который там приводился, нафиг не нужен.

Dasar

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

Marinavo_0507

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

6yrop

Этот код решает задачу суммирования чисел, разделенных пробелом.
Да, но это не полное условие задачи.

Dasar

Если смотреть твой код, то там уже нет ни IndexOf ни TryParse.
конечно, потому что я твердо считаю, что IndexOf в таких задачах подлежит обязательному заменению на итератор, а TryParse с out-параметром подлежит замене на функцию с одним результирующим значением - пусть сложным(например, в виде вспомогательной структуры но главное без out-ов.

6yrop

 
конечно, потому что я твердо считаю, что IndexOf в таких задачах подлежит обязательному заменению на итератор
  

если ставить просто задачу суммирования, то это несомненно так. Мне удивляет, почему опытных программеров, так увлекла сама по себе задача суммирования чисел :grin:
TryParse с out-параметром подлежит замене на функцию с одним результирующим значением - пусть сложным(например, в виде вспомогательной структуры но главное без out-ов.
Почему? Потому что out-параметры плохо сочитаются с лямбдами?

Dasar

Почему? я так с ходу не могу придумать стоящего аргумента.
потому что тогда есть один однородный результат, с которым проще проводить дальнейшие взаимодействия.
возьмем для примера вот такие TryParse:

int.TryParse(string, out int result)

и
такой

int? int.TryParse(string)

вместо них кстати мог быть TryParse, который еще возвращает причину ошибки

bool TryParse(string, out int result, out Exception exc)

TryParseResult TryParse(string)

class TryParse
{
public readonly int Result;
public readonly Exception Error;
public bool IsSuccess {get {return Error != null;}}
}

задача вывести ошибочные лексемы:
в одном случае

var items = str.Split(' ');
Console.WriteLine(items.Where(item => int.TryParse(item) == null).JoinToString(", ";

в другом:

var items = str.Split(' ');
Console.WriteLine(items.Where(item => {var result; return int.TryParse(item, out result);}).JoinToString(", ";

задача: вывести ошибочные лексемы и посчитать сумму на неошибочных

var items = str.Split(' ');
var parsedItems = items.Select(str => new{Source = str, Result = int.TryParse});
Console.Error.WriteLine(parsedItems
.Where(parsedItem => parsedItem.Result == null)
.Select(parsedItem=>parsedItem.Result)
.JoinToString(", ";
Console.WriteLine(parsedItems.Where(parsedItem => parsedItem.Result != null)
.Sum(parsedItem=>parsedItem.Result;

в другом

var items = str.Split(' ');
var parsedItems = items.Select(str =>{int result; bool isParsed = int.TryParse(str, out result);
return new{Source = str, Result = isParsed? (int?)result: (int?)null;}
);
Console.Error.WriteLine(parsedItems.Where(parsedItem => parsedItem.Result == null)
.Select(parsedItem=>parsedItem.Result).JoinToString(", ";
Console.WriteLine(parsedItems.Where(parsedItem => parsedItem.Result != null)
.Sum(parsedItem=>parsedItem.Result;

т.е. out-параметр почти всегда требует больше кода, чем без out-параметров

Dasar

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

6yrop

var items = str.Split(' ');
var parsedItems = items.Select(str =>{int result; bool isParsed = int.TryParse(str, out result);
return new{Source = str, Result = isParsed? (int?)result: (int?)null;}
);
Console.Error.WriteLine(parsedItems.Where(parsedItem => parsedItem.Result == null)
.Select(parsedItem=>parsedItem.Result).JoinToString(", ";
Console.WriteLine(parsedItems.Where(parsedItem => parsedItem.Result != null)
.Sum(parsedItem=>parsedItem.Result;
имхо, так даже лучше читается
 
var parsedItems = items.Select(
str =>
{
int result;
return new
{
Source = str,
IsParsed = int.TryParse(str, out result
Result = result
};
});

Console.Error.WriteLine(parsedItems.Where(parsedItem => parsedItem.IsParsed == false)
.Select(parsedItem => parsedItem.Source).JoinToString(", ";
Console.WriteLine(parsedItems.Where(parsedItem => parsedItem.IsParsed)
.Sum(parsedItem => parsedItem.Result;

6yrop

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

6yrop

Console.Error.WriteLine(parsedItems.Where(parsedItem => parsedItem.Result == null)
  .Select(parsedItem=>parsedItem.Result).JoinToString(", ";
О! тут ты допустил прикольную опечатку вместо parsedItem.Result надо parsedItem.Source. Код даже в рантайме не сообщит об ошибке программиста.
Посмотрим на вариант с использованием моего метода

 

private interface IITem
{
string Source { get; }
bool IsParsed { get; }
int Result { get; }
}

private class ParsedItem : IITem
{
private readonly string str;
private readonly int result;

public ParsedItem(string str, int result)
{
this.str = str;
this.result = result;
}

public string Source
{
get { return str; }
}

public bool IsParsed
{
get { return true; }
}

public int Result
{
get { return result; }
}
}

private class NoParsedItem : IITem
{
private readonly string str;

public NoParsedItem(string str)
{
this.str = str;
}

public string Source
{
get { return str; }
}

public bool IsParsed
{
get { return false; }
}

public int Result
{
get
{
throw new ApplicationException;
}
}
}

static void Main
{
....
var parsedItems = items.Select(
str => IntEx.Parse<IITem>(
str,
i => new ParsedItem(str, i
=> new NoParsedItem(str)

);
Console.Error.WriteLine(parsedItems.Where(parsedItem => parsedItem.IsParsed == false)
.Select(parsedItem => parsedItem.Result).JoinToString(", ";
Console.WriteLine(parsedItems.Where(parsedItem => parsedItem.IsParsed)
.Sum(parsedItem => parsedItem.Result;
}

Да, он более громоздко смотрится. Но он падает с запланированным исключением!

bleyman

Хо хо, а вы ж не в курсе, наверное, что String.Split вообще говоря оверлоаднутый и у него есть версия со StringSplitOptions?

6yrop

Да, он более громоздко смотрится.
Кстати, с Java-style анонимными классами, он бы был короче.

Marinavo_0507

> Хо хо, а вы ж не в курсе, наверное, что String.Split вообще говоря оверлоаднутый и у него есть версия со StringSplitOptions?
О, пока одни холиварами балуются, другие изучают современные технологии. :p

6yrop

это две крайности, и скорее всего как крайности - они обе проигрышные.
Золотые слова :D , услышали бы их наши архитекторы. Они сначала прошлись по одной крайности, теперь заканчиваю двух летний путь по второй крайности, и удивляются что опять как-то не так :p .

Marinavo_0507

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

6yrop

имхо, двигает тот, кто делает что-то свое и успевает при этом посмотреть какие уже стандартные вещи есть/появились, и умудряется свои кубики заменить на похожие стандартные.
Майкрософт обычно делает не кубики, а довольно монолитную конструкцию.

6yrop

Вообще, возможно несколько стратегий.
1. Делать свою остову, у майкрософта брать "кубики", если удается их отделить.
2. Брать за основу майкрософт, ничего в ней не менять. Добовлять фичи строго придерживаясь подхода майкрософт.
3. Брать за основу майкрософт, пытаться некоторые "кубики" заменить своими.
У какого вариант вероятность, быть выигрышным, больше?

Dasar

ну вот же, читал про роботов-разведчиков размером с пчелу
такой же пиар как робот-змея.
я думаю, что робототехника - подраздел автоматики
как человек, который видел профессиональную массовую автоматику изнутри - могу сказать, что там обычно применяются все те же самые "бухгалтерские" технологии: C/C++, Dos, Windows, Java, .Net, Linux.
из нестандартного разве что видел QNX, embedded NT и технологии заточенные под инженеров (что-нибудь типа языка функциональных блоков)
соответственно, в профессиональной массовой роботехники скорее всего применяются все те же самые "бухгалтерские" технологии

Dasar

Хо хо, а вы ж не в курсе, наверное, что String.Split вообще говоря оверлоаднутый и у него есть версия со StringSplitOptions?
да, не в курсе.
меня больше интересовало показать, как бы я решал похожие задачи с разбором чего-либо (не обязательно строки, и не обязательно через родной split и обработкой результата

6yrop

Сишарп-анонимные классы тоже сгодятся :D

var parsedItems = items.Select(
str => IntEx.Parse(
str,
i =>
{
Func<int> func = => i;
return new { Source = str, IsParsed = true, ResultFunc = func };
},
=>
{
Func<int> func = => { throw new ApplicationException; };
return new { Source = str, IsParsed = false, ResultFunc = func };
}
);
Console.Error.WriteLine(parsedItems.Where(parsedItem => parsedItem.IsParsed == false)
.Select(parsedItem => parsedItem.ResultFunc.JoinToString(", ";
Console.WriteLine(parsedItems.Where(parsedItem => parsedItem.IsParsed)
.Sum(parsedItem => parsedItem.ResultFunc;

Т.е. получилось и безопасно и коротко :).

6yrop

я тут был не до конца прав, в случае TryParse эквивалентной записи нет. Такая запись

code:
int result = default(int);
IntEx.Parse(
"123",
i =>
{
result = i;
return true;
},
=>
{
result = default(int);
return false;
};

хуже, чем out параметром, приходится инициализировать переменную, да и переменных получается два i, result.
На самом деле эквивалентная запись есть :) (я как-то забыл про анонимные классы)

var parseResult = IntEx.Parse(
"123",
i => new {Result = i, IsParsed = true},
=> new {Result = default(int IsParsed = false}
;

6yrop

С использованием анонимных классов вот этот длинный можно переписать так

public static Func<TResult> Sum5<TResult>(string str,
Func<int, TResult> successFunc, Func<TResult> failedFunc)
{
int sum = 0;
int toIndex = 0;
while (true)
{
var findController = Util.IndexOf(
str, " ", toIndex,
index =>
{
Action action = => toIndex = index + 1;
return new
{
FoundString = str.Substring(toIndex, index - toIndex
IsContinue = true,
BeforeContinueAction = action
};
},
=>
{
Action action = => { throw new ApplicationException; };
return new
{
FoundString = str.Substring(toIndex
IsContinue = false,
BeforeContinueAction = action
};
};
if (IntEx.Parse(
findController.FoundString,
i =>
{
sum += i;
return true;
},
=> false

== false)
{
return failedFunc;
}
if (findController.IsContinue == false)
{
break;
}
else
{
findController.BeforeContinueAction;
}
}
return => successFunc(sum);
}

Если не учитывать скобочки и пустые строчки, то он не сильно больше , но более устойчив к ошибкам.

6yrop

задача: вывести ошибочные лексемы и посчитать сумму на неошибочных
code:
var items = str.Split(' ');
var parsedItems = items.Select(str => new{Source = str, Result = int.TryParse});
Console.Error.WriteLine(parsedItems
  .Where(parsedItem => parsedItem.Result == null)
  .Select(parsedItem=>parsedItem.Result)
  .JoinToString(", ";
Console.WriteLine(parsedItems.Where(parsedItem => parsedItem.Result != null)
  .Sum(parsedItem=>parsedItem.Result;
    public static class StringHlp
    {
     public static string JoinToString(this IEnumerable<string> items, string separator)
     {
     return string.Join(separator, items.ToArray;
     }
    }
Вот решение, если идти до конца подходом, который упоминается в первом посте треда
 

public static void Do
{
var items = "1q 2qwe 33".Split(' ');

var isFirst = true;
TryParse(items,
item => i => { },
item => =>
{
if (isFirst == false) Console.Error.Write(", ");
else isFirst = false;
Console.Error.Write(item);
});
Console.Error.WriteLine;

var sum = 0;
TryParse(items, item => i => sum += i, item => => { });
Console.WriteLine(sum);
}

private static void TryParse(IEnumerable<string> items,
Func<string, Action<int>> successFunc, Func<string, Action> failedFunc)
{
foreach (var item in items)
IntEx.Parse(item, successFunc(item failedFunc(item;
}

Какой код лучше читается?
И в каком сделаешь меньше опечаток?
В какой безопаснее вносить точечные изменения?
Вопрос: все эти финты с коллекциями, как память расходуют? т.е. если у нас IEnumerable<string> идет как stream из файла или и базы данных.

Fmouse

да всё говно, имхо :)

6yrop

да всё говно, имхо
чё та мне тоже начинает так казаться :D

Dasar

Вопрос: все эти финты с коллекциями, как память расходуют? т.е. если у нас IEnumerable<string> идет как stream из файла или и базы данных.
вопрос не понят

6yrop

Допустим у нас строка сохранена в текстовом файле. Текстовый файл очень большой. Для таких больших файлов мы делаем специальную реализацию метода
IEnumerable<string> Split
Файл не заканчивается полностью в память, а читается последовательно.
Будут ли в памяти создаваться структуры, объем которых пропорционален объему текстового файла, при использовании твоего кода?
Одно место я точно вижу

items.ToArray

но это место не принципиально.

Dasar

Будут ли в памяти создаваться структуры, объем которых пропорционален объему текстового файла, при использовании твоего кода?
при yield-ах - память не будет жраться

rosali

> С таким же успехом можно сделать классы-обертки хоть с десятью выделенными значениями
ну не совсем с тем же, этим классам не будет хватать "стандартности", их придется придумывать заново каждый раз, и соответственно заново каждый раз их изучать при желании воспользоваться.
у меня тут такая идейка появилась - наврядли конешно у еня первого :) - клево бы было если б в "исчислении типов" был такой оператор "|". чтобы как-нибудь так:
class A {...};
class B {...};
(A|B) f {...}
A|B x = f;
if (A a = x) {...}
if (B b = x) {...}
ну вобщем идея понятна.
class A implements A_or_B - не предлагать, это другое, тут автор этих классов должен такой вариант использования предполагать.
или я отстал от жизни, и в C# нынче можно добавить классу интерфейсов "снаружи", ну как методов?

rosali

> при yield-ах
а я правильно понимаю, что yield-ы реализуются переносом фрейма в хип? или неужели там тред отдельный стоит?

Helga87

у меня тут такая идейка появилась - наврядли конешно у еня первого :) - клево бы было если б в "исчислении типов" был такой оператор "|".
в Eiffel такое есть. Большого счастья, как оказалось, не приносит.

rosali

зачем ты меня расстраиваешь :D а я так надеялся...

Fmouse

Он просто невкурил. Такая фенька есть во многих языках (Haskell, Ocaml и т.п. и она очень удобна. Например, для рекурсивного описания типов:
 
data AlgExpr = SimpleNode [Char] | UnaryNode [Char] AlgExpr | BinaryNode [Char] AlgExpr AlgExpr | EmptyNode

rosali

нет ну про хаскель то я знаю, там тоже не совсем то - там надо каждый раз имя конструктора придумывать, этих имен не напасешься, и их дальше писать везде надо. а я именно хочу чтобы преобразование A -> A|B происходило неявно. и обратно тоже (с исключением в случае неудачи).
тогда (возврящаясь к нашим баранам) можно было бы TryParse определить как
int|TryParseError TryParse (...)
а пользоваться можно будет тремя способами - не задумываясь о проблемах:
int x = TryParse(...); // исключение если не запарсилось
немного задумываясь:
case (int x = TryParse(... { // ну или что-то подобное
...
} else {
...
}
и сильно задумываясь:
var res = TryParse(...);
case (int x = res) {
...
} else case (TryParseResult err = res) {
... // узнать из err в каком символе проблема и т.п.
}
По моему клёво придумал. Пойти что ли сделать свой .NET язык :cool:

Dasar

а я правильно понимаю, что yield-ы реализуются переносом фрейма в хип?
что понимается под фреймом?
локальные переменные, которые используются между yield-ов трансформируются в переменные теневого класса

rosali

> что понимается под фреймом?
> локальные переменные ...
ну дада, frame функции, обычно же он на стеке лежит, а тут значит в "теневом классе". блин клевая штука, и вполне простая, почему её раньше в каких-нибудь С++ах не придумали...
недавно кстати видел "итераторы" на чистых Сях. вот нашел, фтыкать Example, это конкретный угар =) кто-нибудь может разобраться чё там вообще? два стека на один тред, да?

Dasar

недавно кстати видел "итераторы" на чистых Сях. вот нашел, фтыкать Example, это конкретный угар =) кто-нибудь может разобраться чё там вообще? два стека на один тред, да?
почти.
резервируем место на стеке, и при переключение в итератор двигаем стек на зарезервированное место, а потом обратно.

Dmitriy82

Здесь нелогично, что эти два класса должны быть разными. Т.е. A|A ты не сделаешь. Отсюда и берутся имена конструкторов в алгебраических типах данных. Наверняка возникнут проблемы при многократном применении, например пишешь A|B, но уже забыл, что давным-давно B было объявлено как C|A.
Если все эти проблемы решать самым топорным путём, можно прийти к следующему. В сишарпе частный случай этого уже есть: Nullable<A> = A | *, где * - одноэлементный тип. Ничего не мешает опредленить дженерик Coproduct<A,B>, и операции с ними сделать какие ты написал.

rosali

а ну то есть в итераторе не может быть рекурсии, как собственно и с yield-ами.
мне вот давно интересно было, почему до сих пор компиляторы программный стек держат на "аппаратном", а не в хипе. неужели настолько медленнее? это раньше для работы со стеком были "специальные" инструкции, а сейчас то внутри давно уже risc, которому вообще без разницы хип или стек, всё кеши решают по сути. а само управление памятью в этом месте можно значительно упростить, ведь можно пользоваться тем фактом, что освобождаться будет по принципу стека.
бонусы же за это вполне себе ощутимые - бесконечный стек в мультитредной программе даже на 32-х битах. ну и просто принципиальная возможность иметь в программе эти стеки миллионами.

rosali

> дженерик Coproduct<A,B>
немного пообщавшись с boost-ом :) у меня укрепилось убеждение, что поддержка понятия на уровне самого языка намного лучше, чем попытки принести в язык то, чего в нем изначально не задумывалось, с помощью "библиотеки". ну если понятие системного уровня я имею в виду. я вот например даже не знаю как сделать, чтобы Coproduct<A,B> и Coproduct<B,A> было одно и то же...
эх мутно это всё, как обычно :)

karkar

Языки с алгебраическими типами и паттерн-матчингом спасут российскую демократию.

agaaaa

специальные инструкции короче

agaaaa

что за алгебраические типы?

Fmouse

 
это пример

Dasar

а ну то есть в итераторе не может быть рекурсии, как собственно и с yield-ами.
с yield-ами рекурсия может быть запросто

static IEnumerable<int> X(int max)
{
for (int i = 0; i < max; ++i)
{
yield return i;
foreach (int x in X(i
yield return x;
}
}
Console.WriteLine(X(5).Select(i => i.ToString.JoinToString(", ";

соответственно и на любой другой реализации итератора можно организовать рекурсию.

Dmitriy82

Я думаю, что дело не в том, что понятие вводится на уровне библиотеки. Просто понятие само по себе сложное: ты попробуй формально и полностью избежав всех неоднозначностей и противоречий, неважно какими средствами и на каком уровне, определить коммутативное дизъюнктивное объединение.
И с другой стороны: Nullable<> в сишарпе сделано исключительно на уровне библиотеки, а то что синтаксический сахар добавлен это мелочи.

rosali

хм, ну да туплю :)
а вот еще чего хотел спросить, а как в этом "теневом" классе control point хранится?

Dasar

обычный int

bleyman

И с другой стороны: Nullable<> в сишарпе сделано исключительно на уровне библиотеки, а то что синтаксический сахар добавлен это мелочи.
Ну да, мелочи.
Если я правильно понимаю (хотя могу и ошибаться, в reference что-то не написано ничего интересного этот "неважный" синтаксический сахар обеспечивает в том числе и возможность сложить два нуллабл инта. У Nullable<T>, видишь ли, оператора "+" как бы нету. И конверсия к T тоже только explicit, причём понятно, зачем так — всё-таки складывая два int?, ты хочешь получить тоже int?, равный нуллу если один или оба операнда равны нуллу, а вовсе не эксепшен.
Без этого синтаксического сахара нуллаблы можно было бы сразу выкидывать на помойку. Если я всё правильно понимаю, конечно.

rosali

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

rosali

брр, а как можно на "обычный int" передать управление?

Dasar

брр, а как можно на "обычный int" передать управление?
управление передается на следующий вызов итератора, а внутри это устроено как обычный автомат - где состояние кодируется как int, и на основе текущего состояния выбирается нужная ветка.
т.е. если полностью разбирать код, то получается следующее (псевдокод):

var iterator = Create;
for (;;)
{
iterator.MoveNext; //вот передача управления
}

void MoveNext
{
switch (state) //вот память о текущем control point-е
{
case 1:
....
state++;
break;
case 2:
....
state++;
break;
case 3:
...
state = 1;
break;
}
}

6yrop

Идея -я — yield и IEnumerable<> — хорошая.
Ее можно совместить с возвращением результата через параметр

static void Main
{
var enumerable = new[] { "1", "2", "qwe", "sddd" };

Console.WriteLine(
Parse<int>(enumerable, item => item.Return, item => => { }).Sum
);

Console.WriteLine(
Parse<string>(enumerable, item => i => { }, item => => item.Return(item.Value.JoinToString(", ")
);
}

static IEnumerable<TResult> Parse<TResult>(IEnumerable<string> enumerable,
Func<IMiscellaniesItem<string, TResult>, Action<int>> successFunc,
Func<IMiscellaniesItem<string, TResult>, Action> failedAction)
{
return enumerable.AsMiscellanies.Select<TResult>(
item => IntEx.Parse(item.Value, successFunc(item failedAction(item;
}

Правда тип пока в C#-е не вы водиться, но хотя бы контролируется.
Оставить комментарий
Имя или ник:
Комментарий: