Страницы

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

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

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

Интерполяция строк в C# 6 без использования $

#c_sharp #строки #интерполяция


В конфигурационном JSON файле используются строки следующего вида:


{
  "logFilePattern" : "{yyyy}.{mm}.{dd}.log"
}


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


string name = "Fred";
Console.WriteLine(String.Format("Name = {0}", name));
// output: "Name = Fred"


С выходом C# 6 версии появилась возможность использовать именованные аргументы (могу
ошибаться в термине) таким образом:


string name = "Fred";
Console.WriteLine($"Name = {name}");
// output: "Name = Fred"


Вопрос: как интерполировать строку с именованными аргументами сохраненую в переменной?


string stringPattern = "Name = {name}";
string name = "Fred";
// ?



Нашел здесь что это не возможно, т.к. код:


string name = "Fred";
string name2 = $"Name = {name}";


, компилятором приводится к:


string name = "fred";
string name2 = String.Format("Name = {0}", name);


Другим словами, это просто синтаксический сахар.
    


Ответы

Ответ 1



UPD Если весь вопрос в том, как сгенерировать имя лог файла, то может проще обойтись таким способом? В конфигурации задаем валидный с т.з. .NET формат для даты: { "logFilePattern" : "yyyy.MM.dd.log" } Затем в коде выкусываем его и получаем имя файла: const string extension = ".log"; var datePattern = logFilePattern.Remove(logFilePattern.IndexOf(extension), extension.Length); DateTime date = DateTime.UtcNow; var logFileName = date.ToString(datePattern) + extension; О задаче в общем Наивная реализация может состоять просто в замене значения: string stringPattern = "Name = {name}"; string name = "Fred"; string formatted = stringPattern.Replace($"{{nameof(name)}}", name); Тут есть два недостатка: код неуниверсальный (хотелось бы иметь отдельный метод, типа FormatString(pattern, value1, value2, ...)) не учитывается возможное эскапирование фигурных скобок (строка "{{name}}" не является шаблоном, однако замена будет произведена) при наличии нескольких значений мы будем плодить лишние строки Метод вида FormatString(pattern, value1, value2, ...), насколько я понимаю, нереален, поскольку нет способа в вызываемом методе узнать имена параметров, с которыми он вызывался. Поэтому можно остановиться на, например, словаре: public static string FormatString(string pattern, IDictionary values) Эскапирование можно обработать регулярным выражением, но учитывая, что параметров может быть несколько, это тоже может быть излишне тяжелой операцией. Поэтому реализация в общем-то зависит от желаемой степени оптимизированности, в идеале надо вручную проходить char[]/List и менять его на ходу. На ночь глядя получилось так: public static string FormatString(string pattern, IDictionary values) { var buffer = pattern.ToList(); int replaceStartIndex = -1; int replaceEndIndex = -1; int index = 0; while (index < buffer.Count) { var character = buffer[index]; if (character == '{') { if (replaceStartIndex == -1) { replaceStartIndex = index; } else { replaceStartIndex = -1; } } else if (character == '}') { if (replaceEndIndex == -1) { replaceEndIndex = index; } else { replaceEndIndex = -1; } } if (replaceStartIndex > -1 && replaceEndIndex > -1) { var key = new string(buffer.Skip(replaceStartIndex + 1).Take(replaceEndIndex - replaceStartIndex - 1).ToArray()); var value = values[key]; var stringValue = value?.ToString(); buffer.RemoveRange(replaceStartIndex, replaceEndIndex - replaceStartIndex + 1); if (stringValue != null) { buffer.InsertRange(replaceStartIndex, stringValue.ToCharArray()); index = replaceStartIndex + stringValue.Length; } else { index = replaceStartIndex; } replaceStartIndex = -1; replaceEndIndex = -1; } else { index++; } } return string.Join("", buffer); } Проверка: static void Main(string[] args) { string stringPattern = "Name = {name}, Age = {age}, Null = {nullString}, Escape = {{escape}}"; string name = "Fred"; int age = 42; string nullString = null; string escape = "You shouldn't see this"; var values = new Dictionary() { [nameof(name)] = name, [nameof(age)] = age, [nameof(nullString)] = nullString, [nameof(escape)] = escape }; string formatted = FormatString(stringPattern, values); Console.WriteLine(formatted); } Вывод: Name = Fred, Age = 42, Null = , Escape = {{escape}}

Ответ 2



Вот тут сказано, что приведенная в топике строка является типом System.FormattableString. По поводу которого читаем что можно получить по индексу нужный инжектированный параметр. И просто можем сформировать новую строку со своим инжектом. Это возможно в рамках передаваемой интерполированной строки в функцию, например.. Возможно таким способом получить нужный результат?!

воскресенье, 29 декабря 2019 г.

Упрощение многочлена (скобки без вложенности)

#математика #любой_язык #sympy #интерполяция


Нахожу многочлен Лагранжа. На выходе получаю выражение вида:

L(x) = - a0(x - x1)(x - x2)(x - x3)(x - x4)(x - x5) + a1(x - x0)(x - x2)(x - x3)(x
- x4)(x - x5) - ... - a5(x - x0)(x - x1)(x - x2)(x - x3)(x - x4)

Необходимо привести к каноническому виду:

L(x) = A0 * x5 + ... + A4 * x + A5

Необходимо реализовать это программно. 

В связи с чем вопрос: есть ли где готовые реализации подобного кода? На любом языке.
Мне бы посмотреть разобраться.  

Уверен, что задача легко решаема, но буду рад любой реализации. Преподаватель внезапно
решил, что ручной вариант его не устраивает и надо программно, ещё и сроки небольшие,
а голова и без того забита вовсю курсовой. 

UPD: Пролистайте до конца - там есть моя версия готовой программы.
Или нажмите сюда.
    


Ответы

Ответ 1



Для .Net существует библиотека под названием MathNet.Symbolics. Конечно, её возможности намного меньше, чем у SymPy, но с вашей задачей тоже справится без проблем. MathNet.Symbolics написана на F#, но вы также можете её использовать на любом другом языке из семейства .Net. К слову, если вы предпочитаете платформу .NET, то вы можете работать с SymPy при помощи IronPython Мне не очень нравится идея вручную задавать выражение, поэтому для удобства я написал простую функцию для его генерации let test n = let y = sprintf "A%i" let l i n = seq { for j in 0..n - 1 do if j <> i then yield sprintf "(x - x%i)" j } let p i = let y' = y i l i n |> String.concat "*" |> sprintf "%s*%s" y' Array.init n p |> String.concat "+" open MathNet.Symbolics let n = 4 let some = test n printfn "%s" some //A0*(x - x1)*(x - x2)*(x - x3)+A1*(x - x0)*(x - x2)*(x - x3)+ //A2*(x - x0)*(x - x1)*(x - x3)+A3*(x - x0)*(x - x1)*(x - x2) let expr = some |> Infix.parseOrUndefined let exp = expr |> Algebraic.expand exp |> Infix.format |> printfn "%s" (* A0*x^3 + A1*x^3 + A2*x^3 + A3*x^3 - A1*x^2*x0 - A2*x^2*x0 - A3*x^2*x0 - A0*x^2*x1 - A2*x^2*x1 - A3*x^2*x1 + A2*x*x0*x1 + A3*x*x0*x1 - A0*x^2*x2 - A1*x^2*x2 - A3*x^2*x2 + A1*x*x0*x2 + A3*x*x0*x2 + A0*x*x1*x2 + A3*x*x1*x2 - A3*x0*x1*x2 - A0*x^2*x3 - A1*x^2*x3 - A2*x^2*x3 + A1*x*x0*x3 + A2*x*x0*x3 + A0*x*x1*x3 + A2*x*x1*x3 - A2*x0*x1*x3 + A0*x*x2*x3 + A1*x*x2*x3 - A1*x0*x2*x3 - A0*x1*x2*x3 *) Раз уж речь зашла о полиномах Лагранжа приведу пример его вычисления по заданным узлам интерполяции (x, y). В качестве тестовых значений возьмем соответствующие из статьи в вики посвященной интерполяционному многочлену Лагранжа. Функция для создания "шаблонного выражения": let generate n = let y = sprintf "y%i" let l i n = seq { for j in 0..n - 1 do if j <> i then yield sprintf "(x - x%i)/(x%i - x%i)" j i j } let p i = l i n |> String.concat "*" |> sprintf "%s*%s" <| y i Array.init n p |> String.concat "+" задаем значения в виде словаря let maps = [ "x0", -1.5; "x1", -0.75; "x2", 0.0; "x3",0.75; "x4",1.5 "y0", -14.1014; "y1",-0.931596;"y2", 0.0;"y3",0.931596; "y4",14.1014 ] |> Map.ofList Заменяем переменные на значения пробегая по словарю let poly = maps |> Map.fold (fun (acc : string) key value -> acc.Replace(key, string value)) (generate n) Преобразуем в выражение и переведем в более читабельный вид: let ex = poly |> Infix.parseOrUndefined ex |> Algebraic.expand |> Infix.format |> printfn "%s" В результате получим следующее выражение: (-1.47747377777778)*x + 4.83484760493827*x^3 Если у вас возникнут какие-либо вопросы связанные с F# или с библиотекой MathNet.Symbolics не стесняйтесь спрашивать. Для удобства можете пинговать меня в F# чате на SO.

Ответ 2



Python + sympy import sympy x = sympy.Symbol('x') xn = sympy.symarray('x', 3) # от x_0 до x_2 an = sympy.symarray('a', 3) # от a_0 до a_2 F = an[0]*(x-xn[0])*(x-xn[1])*(x-xn[2])+an[1]*(x-xn[0])*(x-xn[1])*(x-xn[2])+an[2]*(x-xn[0])*(x-xn[1])*(x-xn[2]) print(F.as_poly()) Результат: Poly(x**3*a_0 + x**3*a_1 + x**3*a_2 - x**2*a_0*x_0 - x**2*a_0*x_1 - x**2*a_0*x_2 - x**2*a_1*x_0 - x**2*a_1*x_1 - x**2*a_1*x_2 - x**2*a_2*x_0 - x**2*a_2*x_1 - x**2*a_2*x_2 + x*a_0*x_0*x_1 + x*a_0*x_0*x_2 + x*a_0*x_1*x_2 + x*a_1*x_0*x_1 + x*a_1*x_0*x_2 + x*a_1*x_1*x_2 + x*a_2*x_0*x_1 + x*a_2*x_0*x_2 + x*a_2*x_1*x_2 - a_0*x_0*x_1*x_2 - a_1*x_0*x_1*x_2 - a_2*x_0*x_1*x_2, x, a_0, a_1, a_2, x_0, x_1, x_2, domain='ZZ')

Ответ 3



Рассмотрим (на примере PHP), как можно добиться результата без подключения внешних библиотек. Полином Лагранжа представляет собой сумму частичных полиномов Lk(x), каждый из которых обращается в нуль во всех узлах, кроме узла с индексом k. Коэффициенты ak при частичных полиномах принято записывать в виде отношения yk / Pk(xk), причём yk - значение полинома в этом узле. Коэффициентами каждого из частичных полиномов Лагранжа являются значения симметрических полиномов в узлах, для вычисления которых можно написать короткую рекурсивную функцию get_symm($ar, $k). В дальнейшей работе можно опираться на функцию poly($ar, $factor), в которой обработка одномерного массива $ar зависит от типа параметра $factor: Если это число с плавающей точкой, то массив $ar на него умножается. Если это такой же одномерный массив, то элементы этих массивов перемножаются. Если $factor - это двумерный массив с той же "внешней" размерностью, что и массив $ar, то вычисляется линейная комбинация его одномерных массивов, коэффициенты которой берутся из массива $ar. Если $factor - это целое число, то вычисляются коэффициенты частичного полинома Лагранжа с таким индексом. А если $factor - строка, то она задаёт функцию над массивом $ar. Реализованы следующие функции: 'symmetric'. Вычисление массива значений всех возможных симметрических полиномов, образуемых числами массива $ar. 'denominators'. Вычисление массива из всех знаменателей Pk(xk). 'degrees'. Вычисление квадратной матрицы, образованной степенями точек массива. 'reduced'. Вычисление коэффициентов полинома по его корням. Пользовательская функция над массивом $ar, заданная вне функции poly(). Перечень опций с примерами выводится в начале работы программы. Такой подход, помимо решения и тестирования поставленной задачи, даёт удобный инструмент для расширения функциональности (интегрирование и дифференцирование полиномов, представление данных и т.д.). Программа на PHP: $points = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]; $coeffs = [-8.68, 83.44, -291.93, 398.63, -196.79, 26.04]; function print_m($text, $arr, $level=0){ $space = str_repeat(" ", $level++); echo "$space$text"; if(gettype($arr)!="array"){ var_dump($arr); return; } $flag = false; foreach($arr as $value) $flag = $flag || (gettype($value)=="array"); foreach($arr as $key => $value) { if(gettype($value) != "array") { echo $flag ? "
$key => $value" : " $value"; } else { print_m("
$space$key => [", $value, $level); echo " ]"; } } $level--; } function get_symm($arr, $k){ // $k-th symmetric polynomial of $arr if($k==1) return array_sum($arr); $el = array_shift($arr); // Shifting of the first value from array $cnt = count($arr); return ($cnt == 1) ? ($el*end($arr)): ($cnt == $k-1) ? ($el*get_symm($arr, $k-1)): $el * get_symm($arr, $k-1) + (get_symm($arr, $k)); } function squares($a){ return $a * $a; } function round8($a){ return round($a, 8); } function poly($ar, $factor = ""){ switch (gettype($factor)) { case 'float': case 'double': // factoring return array_map(function($val)use($factor){ return $val * $factor;}, $ar); case 'array': if(!is_array(end($factor))){ // arrays factoring return array_map(function($a, $b){return $a * $b;}, $ar, $factor); } else { // weighted sum of $factor $sum = array_fill(0, count($ar), 0.0); // sum initialization array_map(function($a, $ff)use(&$sum){ $f = poly($ff, $a); // array factoring array_walk($f, function($val, $key)use(&$sum){$sum[$key] += $val;}); // summation }, $ar, $factor); return $sum; } case 'integer': // k-th Lagrange polynomial by roots unset($ar[$factor]); return poly($ar, 'reduced'); case 'string': switch ($factor) { case 'symmetric': // all symmetric polynomials's values return array_map(function($a)use($ar){return get_symm($ar, $a);}, range(1,count($ar))); case 'denominators': $prod = []; // prod initialization array_walk($ar, function($value, $key)use($ar, &$prod){ $p = 1; array_walk($ar, function($val, $k)use($value, $key, &$p){ if($k != $key) $p *= $value - $val; }); $prod[] = $p; }); return $prod; case 'degrees': // degrees in accordance with the point quantity $cnt = count($ar); $degree = array_fill(0, $cnt, 1.0); $degrees = [$degree]; while(count($degrees) < $cnt) $degrees[] = ($degree = poly($degree, $ar)); return $degrees; case 'reduced': // all reduced polynomial's coefficients by roots return array_merge(array_reverse(poly(poly($ar, -1.0),"symmetric")),[1.0]); default: return array_map($factor, $ar); } default: return $arr; } } $test = [1.0, 2.0, 3.0, 4.0]; $results[] = ["Testing points (in float point form) are: \$test " => $test]; $results[] = ["Array's factoring: poly(\$test, 5.0) " => poly($test, 5.0)]; $results[] = ["Multiplying to 1d array:  poly(\$test, array_reverse(\$test)) " => poly($test, array_reverse($test))]; $results[] = ["Symmetric polynomials calculation:  poly(\$test, 'symmetric') " => poly($test, 'symmetric')]; $results[] = ["Lagrange denominators calculation:  poly(\$test, 'denominators') " => poly($test, 'denominators')]; $results[] = ["Square matrix of degrees:   \$degrees = poly(\$test, 'degrees')
" => $degrees = poly($test, 'degrees')]; $results[] = ["2d array weighting via scalar production:   poly(\$test, \$degrees)" => poly($test, $degrees)]; $results[] = ["Reduced polynomials via its roots:  poly(\$test, 'reduced') " => poly($test, 'reduced')]; $results[] = ["User function appying:  poly(\$test, 'squares') " => poly($test, 'squares')]; foreach ($test as $key => $value) { $results[] = ["Lagrange partial polynomials:  poly(\$test, $key) " => poly($test, $key)]; } print_m("Poly Functions Description:
", $results); $symp[] = ["Lagrange Polynomial's Nodes " => $points]; $symp[] = ["Lagrange Partial Polynomials' Coefficients " => $coeffs]; $parts = []; foreach ($points as $key =>$value){ $part = poly($points, $key); $parts[] = $part; $symp[] = ["Lagrange Partial Polynomial \#$key " => $part]; } $lagrange = poly($coeffs, $parts); $symp[] = ["Lagrange Polynomial's Coefficients " => poly($lagrange, 'round8')]; print_m("

Lagrange Polynomial Symplifying:
", $symp); $denoms = poly($points, 'denominators'); $checking[] = ["Lagrange Denominators " => $denoms]; $values = poly(poly($coeffs, $denoms), 'round8'); $checking[] = ["Polynomial's Values In The Nodes Via Denominators" => $values]; $degrees = poly($points, 'degrees'); $checking[] = ["Degrees of issue Points " => $degrees]; $values2 = poly(poly($lagrange, $degrees), 'round8'); $checking[] = ["Polynomial's Values In The Nodes Via Symplified Polinomial" => $values2]; print_m("

Checking:
", $checking); Результаты: Poly Functions Description:   0 => [    Testing points (in float point form) are: $test => [ 1 2 3 4 ] ]  1 => [    Array's factoring: poly($test, 5.0) => [ 5 10 15 20 ] ]  2 => [    Multiplying to 1d array:  poly($test, array_reverse($test)) => [ 4 6 6 4 ] ]  3 => [    Symmetric polynomials calculation:  poly($test, 'symmetric') => [ 10 35 50 24 ] ]  4 => [    Lagrange denominators calculation:  poly($test, 'denominators') => [ -6 2 -2 6 ] ]  5 => [    Square matrix of degrees:   $degrees = poly($test, 'degrees') => [      0 => [ 1 1 1 1 ]      1 => [ 1 2 3 4 ]      2 => [ 1 4 9 16 ]      3 => [ 1 8 27 64 ] ] ]  6 => [    2d array weighting via scalar production:   poly($test, $degrees) => [ 10 49 142 313 ] ]  7 => [    Reduced polynomials via its roots:  poly($test, 'reduced') => [ 24 -50 35 -10 1 ] ]  8 => [    User function appying:  poly($test, 'squares') => [ 1 4 9 16 ] ]  9 => [    Lagrange partial polynomials:  poly($test, 0) => [ -24 26 -9 1 ] ]  10 => [    Lagrange partial polynomials:  poly($test, 1) => [ -12 19 -8 1 ] ]  11 => [    Lagrange partial polynomials:  poly($test, 2) => [ -8 14 -7 1 ] ]  12 => [    Lagrange partial polynomials:  poly($test, 3) => [ -6 11 -6 1 ] ] Lagrange Polynomial Symplifying:   0 => [    Lagrange Polynomial's Nodes => [ 0 0.2 0.4 0.6 0.8 1 ] ]  1 => [    Lagrange Partial Polynomials' Coefficients => [ -8.68 83.44 -291.93 398.63 -196.79 26.04 ] ]  2 => [    Lagrange Partial Polynomial \#0 => [ -0.0384 0.4384 -1.8 3.4 -3 1 ] ]  3 => [    Lagrange Partial Polynomial \#1 => [ -0 0.192 -1.232 2.84 -2.8 1 ] ]  4 => [    Lagrange Partial Polynomial \#2 => [ -0 0.096 -0.856 2.36 -2.6 1 ] ]  5 => [    Lagrange Partial Polynomial \#3 => [ -0 0.064 -0.624 1.96 -2.4 1 ] ]  6 => [    Lagrange Partial Polynomial \#4 => [ -0 0.048 -0.488 1.64 -2.2 1 ] ]  7 => [    Lagrange Partial Polynomial \#5 => [ -0 0.0384 -0.4 1.4 -2 1 ] ]  8 => [    Lagrange Polynomial's Coefficients => [ 0.333312 1.256224 -0.4096 13.538 -24.428 10.71 ] ] Checking:   0 => [    Lagrange Denominators => [ -0.0384 0.00768 -0.00384 0.00384 -0.00768 0.0384 ] ]  1 => [    Polynomial's Values In The Nodes Via Denominators => [ 0.333312 0.6408192 1.1210112 1.5307392 1.5113472 0.999936 ] ]  2 => [    Degrees of issue Points => [      0 => [ 1 1 1 1 1 1 ]      1 => [ 0 0.2 0.4 0.6 0.8 1 ]      2 => [ 0 0.04 0.16 0.36 0.64 1 ]      3 => [ 0 0.008 0.064 0.216 0.512 1 ]      4 => [ 0 0.0016 0.0256 0.1296 0.4096 1 ]      5 => [ 0 0.00032 0.01024 0.07776 0.32768 1 ] ] ]  3 => [    Polynomial's Values In The Nodes Via Symplified Polinomial => [ 0.333312 0.6408192 1.1210112 1.5307392 1.5113472 0.999936 ] ]

Ответ 4



Спасибо всем за помощь. Особенно @FoggyFinder и @insolor. Выкладываю завершённый и рабочий вариант. Программа по узлам и заданной функции (double f(double x){}) составляет многочлен Лагранжа, приводит его к каноническому виду и вычисляет определённый интеграл (работает, только для числовых значений). using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using MathNet.Symbolics; namespace Polynom { class Program { static void Main(string[] args) { double[] x = { 0, 0.2, 0.4, 0.6, 0.8, 1.0 }; // n - определяется как количество узлов минус один. //double[] x = { -1.5, -0.75, 0, 0.75, 1.5 }; double[] coef = GetCoefLagrange(x); string exp = GetPolynom(coef, x); string simpExp = SimplifyPolynom(exp); var defInt = GetDefInt(GetSimplifyCoef(simpExp), 0, 1); Console.WriteLine("Многочлен Лагранжа:"); Console.WriteLine(exp); Console.WriteLine("---------------------"); Console.WriteLine("Канонический вид:"); Console.WriteLine(simpExp); Console.WriteLine("---------------------"); Console.WriteLine("Определённый интеграл от 0 до 1:"); Console.WriteLine(defInt); Console.ReadLine(); } static double f(double x) { //return x * x * x * x; //return Math.Tan(x); return Math.Exp(Math.Pow(x, 1 / 3) * Math.Sin(Math.PI * x)) / (2 + Math.Cos(Math.PI * x)); } static double[] GetCoefLagrange(double[] x) { int n = x.Length - 1; double[] a = new double[n + 1]; for (int i = 0; i < n + 1; i++) { double den = 1; for (int j = 0; j < n + 1; j++) { if (i != j) { den *= (x[i] - x[j]); } } a[i] = f(x[i]) / den; } return a; } static string GetPolynom(double[] a, double[] x) { string exp = ""; int n = x.Length - 1; var culture = System.Globalization.CultureInfo.InvariantCulture; for (int i = 0; i < n + 1; i++) { if (a[i] < 0) { exp += "("; } exp += a[i].ToString(culture); if (a[i] < 0) { exp += ")"; } for (int j = 0; j < n + 1; j++) { if (j != i) { exp += "*(x - "; if (x[j] < 0) { exp += "("; } exp += x[j].ToString(culture); if (x[j] < 0) { exp += ")"; } exp += ")"; } } if (i != n) { exp += " + "; } } return exp; } static string SimplifyPolynom(string exp) { return Infix.Format(Algebraic.Expand(Infix.ParseOrUndefined(exp))); } static double[] GetSimplifyCoef(string exp) { string[] membr = exp.Split(" + ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); double[] coef = new double[membr.Count() + 1]; for (int i = 0; i < membr.Count(); i++) { string str = membr[i].Split("*".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)[0].Replace('.', ','); string xPow = (membr[i].Split("*".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Length > 1)? membr[i].Split("*".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)[1] : ""; int pow = 0; if (!xPow.Contains('x')) { pow = 0; } if (xPow == "x") { pow = 1; } if (xPow.Contains('x') && xPow != "x") { pow = int.Parse(xPow[xPow.Length - 1].ToString()); } if (str.Contains("(")) { str = str.Substring(1, str.Length - 2); coef[pow] = Math.Round(double.Parse(str), 2); } else { coef[pow] = Math.Round(double.Parse(str), 2); } pow++; } return coef; } static double GetDefInt(double[] A, double from, double to) { string str = ""; var culture = System.Globalization.CultureInfo.InvariantCulture; for (int i = 0; i < A.Count(); i++) { str += (A[i] / (i + 1)).ToString(culture) + "*x^" + (i + 1); if (i != A.Count() - 1) { str += " + "; } } var exp = Infix.ParseOrUndefined(str); var x = new Dictionary { { "x", 0.0 } }; var resFrom = Evaluate.Evaluate(x, exp); x = new Dictionary { { "x", 1.0 } }; var resTo = Evaluate.Evaluate(x, exp).RealValue; return resTo - resFrom.RealValue; } } } Вывод консоли: Многочлен Лагранжа: (-8.68055555555555)*(x - 0.2)*(x - 0.4)*(x - 0.6)*(x - 0.8)*(x - 1) + 83.4365435984129*(x - 0)*(x - 0.4)*(x - 0.6)*(x - 0.8)*(x - 1) + (-291.931019062828)*(x - 0)*(x - 0.2)*(x - 0.6)*(x - 0.8)*(x - 1) + 398.628301975219*(x - 0)*(x - 0.2)*(x - 0.4)*(x - 0.8)*(x - 1) + (-196.79094312252)*(x - 0)*(x - 0.2)*(x - 0.4)*(x - 0.6)*(x - 1) + 26.0416666666667*(x - 0)*(x - 0.2)*(x - 0.4)*(x - 0.6)*(x - 0.8) --------------------- Канонический вид: 0.333333333333333 + 1.2551290418413*x + (-0.40261625087755)*x^2 + 13.5213484261595*x^3 + (-24.4111890498517)*x^4 + 10.7039944993951*x^5 --------------------- Определённый интеграл от 0 до 1: 1,108

четверг, 21 февраля 2019 г.

Интерполяция строк в C# 6 без использования $

В конфигурационном JSON файле используются строки следующего вида:
{ "logFilePattern" : "{yyyy}.{mm}.{dd}.log" }
Для замены используется множество разных текстовых заполнителей: вычисляемые пути файловой системы, которые зависят от пользователя и т.д.. Предполагается их замена и для этого в предыдущих версиях C# использовалось составное форматирование (могу ошибаться в термине) таким образом:
string name = "Fred"; Console.WriteLine(String.Format("Name = {0}", name)); // output: "Name = Fred"
С выходом C# 6 версии появилась возможность использовать именованные аргументы (могу ошибаться в термине) таким образом:
string name = "Fred"; Console.WriteLine($"Name = {name}"); // output: "Name = Fred"
Вопрос: как интерполировать строку с именованными аргументами сохраненую в переменной?
string stringPattern = "Name = {name}"; string name = "Fred"; // ?
Нашел здесь что это не возможно, т.к. код:
string name = "Fred"; string name2 = $"Name = {name}";
, компилятором приводится к:
string name = "fred"; string name2 = String.Format("Name = {0}", name);
Другим словами, это просто синтаксический сахар.


Ответ

UPD
Если весь вопрос в том, как сгенерировать имя лог файла, то может проще обойтись таким способом? В конфигурации задаем валидный с т.з. .NET формат для даты:
{ "logFilePattern" : "yyyy.MM.dd.log" }
Затем в коде выкусываем его и получаем имя файла:
const string extension = ".log"; var datePattern = logFilePattern.Remove(logFilePattern.IndexOf(extension), extension.Length); DateTime date = DateTime.UtcNow; var logFileName = date.ToString(datePattern) + extension;

О задаче в общем
Наивная реализация может состоять просто в замене значения:
string stringPattern = "Name = {name}"; string name = "Fred"; string formatted = stringPattern.Replace($"{{nameof(name)}}", name);
Тут есть два недостатка:
код неуниверсальный (хотелось бы иметь отдельный метод, типа FormatString(pattern, value1, value2, ...)) не учитывается возможное эскапирование фигурных скобок (строка "{{name}}" не является шаблоном, однако замена будет произведена) при наличии нескольких значений мы будем плодить лишние строки
Метод вида FormatString(pattern, value1, value2, ...), насколько я понимаю, нереален, поскольку нет способа в вызываемом методе узнать имена параметров, с которыми он вызывался. Поэтому можно остановиться на, например, словаре:
public static string FormatString(string pattern, IDictionary values)
Эскапирование можно обработать регулярным выражением, но учитывая, что параметров может быть несколько, это тоже может быть излишне тяжелой операцией. Поэтому реализация в общем-то зависит от желаемой степени оптимизированности, в идеале надо вручную проходить char[]/List и менять его на ходу.
На ночь глядя получилось так:
public static string FormatString(string pattern, IDictionary values) { var buffer = pattern.ToList(); int replaceStartIndex = -1; int replaceEndIndex = -1;
int index = 0; while (index < buffer.Count) { var character = buffer[index]; if (character == '{') { if (replaceStartIndex == -1) { replaceStartIndex = index; } else { replaceStartIndex = -1; } } else if (character == '}') { if (replaceEndIndex == -1) { replaceEndIndex = index; } else { replaceEndIndex = -1; } }
if (replaceStartIndex > -1 && replaceEndIndex > -1) { var key = new string(buffer.Skip(replaceStartIndex + 1).Take(replaceEndIndex - replaceStartIndex - 1).ToArray()); var value = values[key]; var stringValue = value?.ToString(); buffer.RemoveRange(replaceStartIndex, replaceEndIndex - replaceStartIndex + 1); if (stringValue != null) { buffer.InsertRange(replaceStartIndex, stringValue.ToCharArray()); index = replaceStartIndex + stringValue.Length; } else { index = replaceStartIndex; } replaceStartIndex = -1; replaceEndIndex = -1; } else { index++; } }
return string.Join("", buffer); }
Проверка:
static void Main(string[] args) { string stringPattern = "Name = {name}, Age = {age}, Null = {nullString}, Escape = {{escape}}"; string name = "Fred"; int age = 42; string nullString = null; string escape = "You shouldn't see this"; var values = new Dictionary() { [nameof(name)] = name, [nameof(age)] = age, [nameof(nullString)] = nullString, [nameof(escape)] = escape }; string formatted = FormatString(stringPattern, values); Console.WriteLine(formatted); }
Вывод:
Name = Fred, Age = 42, Null = , Escape = {{escape}}