5
static void Main() 
{ 
  double theta; //угол в радианах 
  for (theta = 0.1; theta <= 1.0; theta = theta + 0.1) 
  { 
    Console.WriteLine("Синус угла {0} равен {1}", theta, Math.Round(Math.Sin(theta),2)); 
    Console.WriteLine("Косинус угла {0} равен {1}", theta, Math.Round(Math.Cos(theta),2)); 
    Console.WriteLine("Тангенс угла {0} равен {1}", theta, Math.Round(Math.Tan(theta),2)); 
  }
} 

Здравствуйте, уважаемые. Сверху код вычисляет синус косинус и тангенс теты. На консоль выводится ответ такого типа (примерно):

Синус....
Косинус...
Тангенс...
Синус....
Косинус....
Тангенс....
...........

А мне нужен ответ типа:

Синус....
Синус....
Синус....
.......
Косинус....
косинус....
Косинус....
.......
Тангенс....
Тангенс....
Тангенс....
.......

Я могу использовать еще два раза функцию for но мне кажется что это не правильно. Есть идеи как сделать это без нубских копирований? Думаю в языке есть методы чтобы использовать функцию несколько раз без лишней писанины! Но я не знаю! Буду рад любым ответам!

Denis Bubnov
  • 13,958
Tobi
  • 75

7 Answers7

12

Ну можно поизвращаться.

void Display(Func<double, double> f, string name)
{
    for (double theta = 0.1; theta <= 1.0; theta = theta + 0.1)
        Console.WriteLine($"{name} угла {theta:F1} равен {f(theta):F2}");
}
Display(Math.Sin, "Синус");
Display(Math.Cos, "Косинус");
Display(Math.Tan, "Тангенс");

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

Окей, что у нас отличается? Две вещи: вызываемая функция и строка с её именем. Строку передать просто, а как передать функцию? Оказывается, функции тоже можно записывать в переменные! Для функции, которая берёт аргумент типа double и возвращает значение типа double, тип переменной будет Func<double, double>. Объявляем параметр f такого типа. Теперь можно в метод Display передать саму функцию Math.Sin, как это и написано в примере кода.

Да, и я ещё округляю до двух знаков после запятой не через Math.Round (у него могут быть потенциальные проблемы, ведь не каждое число можно представить в типе double точно с двумя знаками после запятой), а через указание формата вывода: F2 означает, что я хочу при выводе получить 2 знака после запятой.

А ещё я пользуюсь модной фишкой под названием интерполяция строк.

VladD
  • 206,799
  • 4
    А в чем извращение? :-) стандартный подход: "Выделение метода" – Grundy Feb 09 '17 at 15:26
  • 1
    @Grundy: Извращение в том, что для такой простой задачи это слишком много кода. И целая вспомогательная функция. Возможно, три цикла проще. – VladD Feb 09 '17 at 16:50
  • 2
    Мне тоже это решение по душе, да и строчек кода не больше чем в остальных, при желании начальное значение, конечное значение и шаг цикла можно тоже перенести в параметры и получить готовый "библиотечный" метод :) Да, еще и выходной поток в параметре передавать :) – Андрей NOP Feb 10 '17 at 03:58
  • А я ничего не понял... :( Хотел бы узнать как все работает тут но думаю понадобится много букаф и времени... Вашего времени... :D Если у вас будет свободное время и настроение пожалуйста пожуйте этот код и покормите маленьких, глупых птенчиков. XD – Tobi Feb 10 '17 at 09:07
  • @Tobi: Расширил ответ, читайте. – VladD Feb 10 '17 at 09:50
  • 1
    Проблема такого подхода - в делегат выносятся слишком простая функция. Накладные расходы будут высокие... – Pavel Mayorov Feb 10 '17 at 10:05
  • @PavelMayorov: Ну уж вам придираться. Не думаю, что накладные расходы на вызов делегата будут сравнимы с расходами на Console.WriteLine.Ну даже если это и не было так, всё равно устранение дубляжа кода может быть важнее. (Особенно в дидактических целях.) За исключением реально узких мест (стандартный пассаж о профилировании и предварительной оптимизации не пишу). – VladD Feb 10 '17 at 10:15
  • @PavelMayorov: Я вот скорее сомневаюсь, проще ли изменённый код для читателя по сравнению с первоначальным. То есть я бы скорее говорил о повышении не вычислительной, а когнитивной сложности кода. – VladD Feb 10 '17 at 10:18
  • @PavelMayorov Вы про минусы этого кода?Можно подетальнее?) – Tobi Feb 10 '17 at 10:19
4

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

string s1 = "", s2 = "", s3 = "";
for (theta = 0.1; theta <= 1.0; theta = theta + 0.1) 
{ 
    s1 += string.Format("Синус угла {0} равен {1}\n", theta, Math.Round(Math.Sin(theta), 2)); 
    s2 += string.Format("Косинус угла {0} равен {1}\n", theta, Math.Round(Math.Cos(theta), 2)); 
    s3 += string.Format("Тангенс угла {0} равен {1}\n", theta, Math.Round(Math.Tan(theta), 2)); 
}
Console.Write(s1 + s2 + s3);
  • Это работает и вроде понятно но такая логика чужда мне.Сверху код со switch-ем намного приятнее, но ваш код мне тоже понравился.Хотел и вам поставить зеленую галочку но можно только одному. :( Если будет время вкратце напишите зачем там \n .Спасибо что уделили мне немного вашего времени. :3 – Tobi Feb 09 '17 at 21:43
  • 1
    \n - Перевод строки – Андрей NOP Feb 10 '17 at 03:54
  • 2
    Сложение строк... – Qwertiy Feb 10 '17 at 09:54
  • Хороший вариант, только вместо string используйте StringBuilder – Ev_Hyper Feb 10 '17 at 12:48
  • Вообще, конечно, да. Но при 10 итерациях разницы никто не заметит. – Андрей NOP Feb 10 '17 at 15:20
4

Вывод правильнее сделать в разных циклах.

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

И ещё for - это не функция, а конструкция языка.

http://ideone.com/NztJ2O

using System;
using System.Collections.Generic;
using static System.Math;

public class Test
{
    private class Vals
    {
        public double Theta;
        public double Sin;
        public double Cos;
        public double Tan;
    }

    public static void Main()
    {
        var data = new List<Vals>();

        for (double theta = 0.1; theta <= 1.0; theta = theta + 0.1)
            data.Add(new Vals() { Theta = theta, Sin = Sin(theta), Cos = Cos(theta), Tan = Tan(theta) });

        foreach (var val in data)
            Console.WriteLine("Синус угла {0} равен {1}", val.Theta, Math.Round(val.Sin, 2)); 

        foreach (var val in data)
            Console.WriteLine("Косинус угла {0} равен {1}", val.Theta, Math.Round(val.Cos, 2)); 

        foreach (var val in data)
            Console.WriteLine("Тангенс угла {0} равен {1}", val.Theta, Math.Round(val.Tan, 2)); 
    }
}
Qwertiy
  • 123,725
  • Вот, голосую за перенесение зелёной галочки сюда. – vp_arth Feb 10 '17 at 10:07
  • @vp_arth почему? – Ev_Hyper Feb 10 '17 at 12:49
  • Это решение реализует мой комментарий к посту) Основная причина - наличие разделения логики и вывода. – vp_arth Feb 10 '17 at 13:31
  • Нет, зеленую он не получит... -.- Для меня главное меньше кода.И еще что бы я хоть что то понял в этом коде.Новичок задал вопрос и решить проблему надо на его уровне.Я ничего не понял из этого.Поставлю лайк но это мне это не интересно. – Tobi Feb 14 '17 at 20:05
4

http://ideone.com/h7Ih1l

using System;
using System.Linq;
using static System.Math;

public class Test
{
    public static void Main()
    {
        var data = Enumerable.Range(0, 10).Select(x => x/10.0);

        Console.WriteLine(String.Join("\n", data.Zip(data.Select(Sin), (x, y) => String.Format("Синус угла {0} равен {1}", x, y))));
        Console.WriteLine(String.Join("\n", data.Zip(data.Select(Cos), (x, y) => String.Format("Косинус угла {0} равен {1}", x, y))));
        Console.WriteLine(String.Join("\n", data.Zip(data.Select(Tan), (x, y) => String.Format("Тангенс угла {0} равен {1}", x, y))));
    }
}
Qwertiy
  • 123,725
  • Zip - это типа MapReduce? – vp_arth Feb 14 '17 at 19:48
  • @vp_arth, интересная аналогия. Возможно. Он синхронизирует две последовательности и поэлементно отдаёт в функцию. – Qwertiy Feb 14 '17 at 19:49
3

Решение в лоб:

double theta; //угол в радианах 
for (int foo = 0; foo < 3; foo++) {
  for (theta = 0.1; theta <= 1.0; theta = theta + 0.1) 
  {
    switch (foo) {
     case 0: Console.WriteLine("Синус угла {0} равен {1}", theta, Math.Round(Math.Sin(theta), 2)); break;
     case 1: Console.WriteLine("Косинус угла {0} равен {1}", theta, Math.Round(Math.Cos(theta), 2)); break;
     case 2: Console.WriteLine("Тангенс угла {0} равен {1}", theta, Math.Round(Math.Tan(theta), 2)); break;
    }
  }
}

Вариация на тему лямбд:

Func<string, Func<double, double>, int> solve = (string funcName, Func<double, double> func) => {
  for (double theta = 0.1; theta <= 1.0; theta = theta + 0.1) 
    Console.WriteLine(String.Format("{0} угла {1} равен {2}", funcName, theta, Math.Round(func(theta), 2)));
  return 0;
};

solve("Синус", Math.Sin);
solve("Косинус", Math.Cos);
solve("Тангенс", Math.Tan);
vp_arth
  • 27,179
  • 1
    Это мне больше понравилось!Просто и понятно(да, я не понял что такое switch и case, break, но суть не в этом :D ). – Tobi Feb 09 '17 at 19:20
  • очень красивое решение! – Senior Pomidor Feb 09 '17 at 19:47
  • 4
    Брррррррррррррррррррр – Qwertiy Feb 10 '17 at 09:52
  • @Qwertiy Почему же бррр?)По мне лучше бы в языке сделали возможным вызывать функции заново несложным, маленьким кодом(хотя бы внутри класса.Это только идеи новичка и может показаться глупым и не только показаться... :D ) типа: for(1)(theta = 0.1; ........) Console.WriteLine(......); run for1; //маленький код для вызова функции Console.WriteLine(....); Вот и все.Я просто не могу понять как в язке нету возможности вызывать уже написанные функции и т.п. вещи(конечно же без лишней писанины и головной боли). – Tobi Feb 10 '17 at 10:41
  • 1
    @Tobi, в языке есть почти всё, что нужно. И вызывать можно - тут есть ответы. А брр потому что делать switch по индексу внутри for - это жесть. Вот представь, что у тебя есть 3 предмета, которые надо доставить в 3 разные квартиры в 3 домах. Теперь ты заходишь в первый дом, находишь в нём квартиру, написанную на первой карточке, встаёшь около звонка и сверяешь, а дом на карточке совпадает с домом, в котором ты нашёл квартиру? Упс, нет, пошли в следующий. Нашли. Но в третий мы всё равно сходим и проверим, что он не совпадёт. И снова в первый, чтобы вторую посылку отнести... И так 9 раз. Годится? – Qwertiy Feb 10 '17 at 13:17
  • Нашли. Но в третий мы всё равно сходим и проверим - вот тут перебор, это было бы так, если написать три if'a)) – vp_arth Feb 10 '17 at 13:35
  • @Qwertiy Немного долгий но логичный метод( поэтому зеленую стрелку получил только он). :D Ну если в языке есть все что нужно то напишите код где не меняют мой собственный.Тут внизу используют void Display.Но у меня void Main.Некоторые изменили весь код(заново написали).А мне хотелось вызвать функцию for еще пару раз но меньшими затратами(я про буквы). – Tobi Feb 10 '17 at 13:46
  • @Tobi, void Display определяется не вместо, а рядом с(или внутри) Main – vp_arth Feb 10 '17 at 13:47
  • @vp_arth Может быть.)Я еще учу С#.Просто я хотел столбцы как написал в топе.Моими знаниями мозг придумал только добавить еще 2 for.Но в голову пришла идея что можно призывать заранее написанную функцию.И это сделает мой код идеальным. :D Думаю я свой вопрос сформулировал не правильно. – Tobi Feb 10 '17 at 14:03
  • @vp_arth, да, в третий не пойдём, т. к. break. Но на втором заходе во второй всё равно пойдём. – Qwertiy Feb 14 '17 at 19:24
  • @Tobi, я уже сказал, for - это не функция. И ответ я уже написал. Сейчас могу ещё один написать, другой. – Qwertiy Feb 14 '17 at 19:24
  • @Qwertiy А что это?Оператор? – Tobi Feb 14 '17 at 19:55
  • @Tobi, "оператор" как "statement" - можно назвать (хотя, полагаю, это будет подразумевать и тело), "оператор" как "operator" - нет. Это конструкция языка. Если уж хочется подставить перед "for" какое-то существительное, стоит выбрать "цикл". – Qwertiy Feb 14 '17 at 19:57
  • @Qwertiy Спасибо за пояснение.Я ваши ответы прочитал только сейчас.Хз почему на программу не пришло оповещение раньше... – Tobi Feb 14 '17 at 20:13
3

Дополню уже существующие варианты. Если не знакомы с делегатами, то можно написать 3 метода, каждый метод считает конкретную функцию и выводит результат, но советую обратить внимание на делегаты.

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

Func<double, double>[] functions = { Math.Sin, Math.Cos, Math.Tan };
string[] funcName = { "Синус", "Косинус", "Тангенс" };
for (int i = 0; i < functions.Length; i++)
    for (double theta = 0.1; theta <= 1.0; theta = theta + 0.1) 
        Console.WriteLine("{0} угла {1} равен {2}", funcName[i], theta, Math.Round(functions[i](theta),2)) ;
2

Можно вот таким способом реализовать решение Вашей задачи. С помощью только одного цикла for:

public static void CalculateAndPrintResultByName(string name)
{
    for (double theta = 0.1; theta <= 1.0; theta = theta + 0.1)
    {
        switch (name)
        {
            case "Синус":
                Console.WriteLine("Синус угла {0} равен {1}", 
                    theta, Math.Round(Math.Sin(theta), 2));
                break;
            case "Косинус":
                Console.WriteLine("Косинус угла {0} равен {1}", 
                    theta, Math.Round(Math.Cos(theta), 2));
                break;
            case "Тангенс":
                Console.WriteLine("Тангенс угла {0} равен {1}", 
                    theta, Math.Round(Math.Tan(theta), 2));
                break;
            default:
                Console.WriteLine("Функция {0} не определена", name);
                return;
        }
    }
}

Ну и вызов метода:

CalculateAndPrintResultByName("Синус");
CalculateAndPrintResultByName("Косинус");
CalculateAndPrintResultByName("Тангенс");
Denis Bubnov
  • 13,958