diff --git a/course-2021-1/exercises/02-adventure-time/AdventureTime/AdventureTime/Program.cs b/course-2021-1/exercises/02-adventure-time/AdventureTime/AdventureTime/Program.cs
index af4efcae..79114770 100644
--- a/course-2021-1/exercises/02-adventure-time/AdventureTime/AdventureTime/Program.cs
+++ b/course-2021-1/exercises/02-adventure-time/AdventureTime/AdventureTime/Program.cs
@@ -1,9 +1,39 @@
-namespace AdventureTime
+using System;
+
+namespace AdventureTime
{
internal class Program
{
private static void Main()
{
+ DateTime dt = Time.WhatTimeIsIt();
+ DateTime udt = Time.WhatTimeIsItInUtc();
+
+ Console.WriteLine(dt);
+ Console.WriteLine(udt);
+
+ // all the same
+ Console.WriteLine(Time.SpecifyKind(dt, DateTimeKind.Utc));
+ Console.WriteLine(Time.SpecifyKind(dt, DateTimeKind.Local));
+ Console.WriteLine(Time.SpecifyKind(dt, DateTimeKind.Unspecified));
+
+ var roundtrip = Time.ToRoundTripFormatString(dt);
+ Console.WriteLine(roundtrip);
+ Console.WriteLine(Time.ParseFromRoundTripFormat(roundtrip));
+
+ Console.WriteLine(Time.AddTenSeconds(dt));
+ Console.WriteLine(Time.AddTenSecondsV2(dt));
+
+ Console.WriteLine(Time.GetHoursBetween(udt, dt));
+ Console.WriteLine(Time.GetTotalMinutesInThreeMonths());
+
+ Console.WriteLine(Time.GetAdventureTimeDurationInMinutes_ver0_Dumb());
+ Console.WriteLine(Time.GetGenderSwappedAdventureTimeDurationInMinutes_ver0_Dumb());
+ Console.WriteLine(Time.GetAdventureTimeDurationInMinutes_ver1_FeelsSmarter());
+ Console.WriteLine(Time.GetAdventureTimeDurationInMinutes_ver3_NodaTime());
+
+ Console.WriteLine(Time.AreEqualBirthdays(dt, udt));
+ Console.WriteLine(Time.AreEqualBirthdays(dt, udt.AddDays(1)));
}
}
}
diff --git a/course-2021-1/exercises/02-adventure-time/AdventureTime/AdventureTime/Time.cs b/course-2021-1/exercises/02-adventure-time/AdventureTime/AdventureTime/Time.cs
index 45de70d1..c9c18a3a 100644
--- a/course-2021-1/exercises/02-adventure-time/AdventureTime/AdventureTime/Time.cs
+++ b/course-2021-1/exercises/02-adventure-time/AdventureTime/AdventureTime/Time.cs
@@ -14,7 +14,7 @@ internal static class Time
///
public static DateTime WhatTimeIsIt()
{
- throw new NotImplementedException();
+ return DateTime.Now;
}
///
@@ -22,7 +22,7 @@ public static DateTime WhatTimeIsIt()
///
public static DateTime WhatTimeIsItInUtc()
{
- throw new NotImplementedException();
+ return DateTime.UtcNow;
}
///
@@ -36,22 +36,28 @@ public static DateTime SpecifyKind(DateTime dt, DateTimeKind kind)
/*
Подсказка: поищи в статических методах DateTime.
*/
- throw new NotImplementedException();
+ return DateTime.SpecifyKind(dt, kind);
}
///
- /// Конвертирует объект в эквивалентное ему строковое представление времени в формате ISO 8601 (aka round-trip format).
+ /// Конвертирует объект в эквивалентное ему
+ /// строковое представление времени в формате ISO 8601 (aka round-trip
+ /// format).
///
/// Объект для конвертации в строку.
/// Строковое представление времени в формате ISO 8601.
public static string ToRoundTripFormatString(DateTime dt)
{
/*
- Обязательно поиграйся и посмотри на изменение результата в зависимости от dt.Kind (для этого тебе поможет метод выше).
- Ну и на будущее запомни этот прекрасный строковый формат представления времени - он твой бро!
- Название запоминать не нужно, просто помни, что для передачи значения в виде строки, выбирать лучше инвариантные относительно сериализации/десериализации форматы.
+ Обязательно поиграйся и посмотри на изменение результата в
+ зависимости от dt.Kind (для этого тебе поможет метод выше). Ну
+ и на будущее запомни этот прекрасный строковый формат
+ представления времени - он твой бро! Название запоминать не
+ нужно, просто помни, что для передачи значения в виде строки,
+ выбирать лучше инвариантные относительно
+ сериализации/десериализации форматы.
*/
- throw new NotImplementedException();
+ return dt.ToString("s", System.Globalization.CultureInfo.InvariantCulture);
}
///
@@ -62,10 +68,11 @@ public static string ToRoundTripFormatString(DateTime dt)
public static DateTime ParseFromRoundTripFormat(string dtStr)
{
/*
- Поиграйся и проверь, что round-trip действительно round-trip, т.е. туда-обратно равно оригиналу (для туда воспользуйся предыдущим методом).
- Проверь для всех значений DateTime.Kind.
+ Поиграйся и проверь, что round-trip действительно round-trip,
+ т.е. туда-обратно равно оригиналу (для туда воспользуйся
+ предыдущим методом). Проверь для всех значений DateTime.Kind.
*/
- throw new NotImplementedException();
+ return DateTime.Parse(dtStr, null, System.Globalization.DateTimeStyles.RoundtripKind);
}
///
@@ -74,10 +81,12 @@ public static DateTime ParseFromRoundTripFormat(string dtStr)
public static DateTime ToUtc(DateTime dt)
{
/*
- Eсли воспользуешься нужным методом, то напоминаю, что результат его работы зависит от dt.Kind.
- В случае dt.Kind == Unspecified предполагается, что время локальное, т.е. результат работы в случае Local и Unspecified совпадают. Такие дела
+ Eсли воспользуешься нужным методом, то напоминаю, что результат
+ его работы зависит от dt.Kind. В случае dt.Kind == Unspecified
+ предполагается, что время локальное, т.е. результат работы в
+ случае Local и Unspecified совпадают. Такие дела
*/
- throw new NotImplementedException();
+ return dt.ToUniversalTime();
}
///
@@ -88,7 +97,7 @@ public static DateTime ToUtc(DateTime dt)
public static DateTime AddTenSeconds(DateTime dt)
{
// здесь воспользуйся методами самого объекта и заодно посмотри какие еще похожие есть
- throw new NotImplementedException();
+ return dt.AddSeconds(10);
}
///
@@ -99,10 +108,13 @@ public static DateTime AddTenSeconds(DateTime dt)
public static DateTime AddTenSecondsV2(DateTime dt)
{
/*
- Ну а здесь воспользуйся сложением с TimeSpan. Обрати внимание, что помимо конструктора, у класса есть набор полезных статических методов-фабрик.
- Обрати внимание, что у TimeSpan нет статических методов FromMonth, FromYear. Как думаешь, почему?
+ Ну а здесь воспользуйся сложением с TimeSpan. Обрати внимание,
+ что помимо конструктора, у класса есть набор полезных
+ статических методов-фабрик. Обрати внимание, что у TimeSpan нет
+ статических методов FromMonth, FromYear. Как думаешь, почему?
*/
- throw new NotImplementedException();
+ var ten_sec = new TimeSpan(0, 0, 10);
+ return dt.Add(ten_sec);
}
///
@@ -118,7 +130,7 @@ public static int GetHoursBetween(DateTime dt1, DateTime dt2)
2) Проверь, учитывается ли Kind объектов при арифметических операциях.
3) Подумай, почему возвращаемое значение может отличаться от действительности.
*/
- throw new NotImplementedException();
+ return Convert.ToInt32(Math.Abs((dt2 - dt1).TotalHours));
}
///
@@ -127,7 +139,8 @@ public static int GetHoursBetween(DateTime dt1, DateTime dt2)
public static int GetTotalMinutesInThreeMonths()
{
// ну тут все просто и очевидно, если сделал остальные и подумал над вопросами в комментах.
- throw new NotImplementedException();
+ var three_month = new TimeSpan(3*30, 0, 0, 0);
+ return Convert.ToInt32(three_month.TotalSeconds);
}
#region Adventure time saga
@@ -136,27 +149,41 @@ public static int GetTotalMinutesInThreeMonths()
/// Возвращает количество минут, проведенных в пути из Москвы в Лондон.
///
///
- /// Финн и Джейк, плотно поужинав, решили, что спать для слабаков и настало время приключений, поэтому быстро собрали вещи, уложили спать БиМО и отправились из воображаемой Москвы в воображаемый Лондон верхом на леди Ливнероге.
- /// Сколько минут они провели в пути, если Москву они покинули 28.03.2010 в 02:15 по местному времени, а в Лондон прибыли в 28.03.2010 в 02:15 по местному?
+ /// Финн и Джейк, плотно поужинав, решили, что спать для слабаков и
+ /// настало время приключений, поэтому быстро собрали вещи, уложили
+ /// спать БиМО и отправились из воображаемой Москвы в воображаемый
+ /// Лондон верхом на леди Ливнероге. Сколько минут они провели в пути,
+ /// если Москву они покинули 28.03.2010 в 02:15 по местному времени, а в
+ /// Лондон прибыли в 28.03.2010 в 02:15 по местному?
///
public static int GetAdventureTimeDurationInMinutes_ver0_Dumb()
{
/*
- Как ты понимаешь, время выбрано не просто так, но для начала давай прикинемся совсем наивными.
- Лондон находится в часовом поясе +0 (GMT), а Москва в +3 (MSK). Воспользуйся DateTimeOffset, чтобы задать правильное время, и посчитай разницу в минутах. Посмотри на результат.
- Держи, заготовочку для копипасты:
+ Как ты понимаешь, время выбрано не просто так, но для начала
+ давай прикинемся совсем наивными. Лондон находится в часовом
+ поясе +0 (GMT), а Москва в +3 (MSK). Воспользуйся
+ DateTimeOffset, чтобы задать правильное время, и посчитай
+ разницу в минутах. Посмотри на результат. Держи, заготовочку
+ для копипасты:
- 2010, 3, 28, 2, 15, 0
*/
- throw new NotImplementedException();
+ var london_t = new DateTimeOffset(2010, 3, 28, 2, 15, 0, TimeSpan.FromHours(0));
+ var moscow_t = new DateTimeOffset(2010, 3, 28, 2, 15, 0, TimeSpan.FromHours(3));
+
+ return Convert.ToInt32(Math.Abs((moscow_t - london_t).TotalMinutes));
}
///
/// Возвращает количество минут, проведенных в пути из Москвы в Лондон.
///
///
- /// Фионна и Кейк, поужинав заварными пироженками от принца Жвачки, решили, что они еще слишком молоды чтобы спать по ночам и сейчас самое время для приключений! Дамы собрали вещи, уложили спать БиМО и отправились из
- /// другой воображаемой Москвы в другой воображаемый Лондон верхом на лорде Монохроме.
- /// Сколько минут они провели в путешествии, если Москву они покинули 28.03.2010 в 03:15 по местному времени, а в Лондон прибыли в 28.03.2010 в 01:15 по местному?
+ /// Фионна и Кейк, поужинав заварными пироженками от принца Жвачки,
+ /// решили, что они еще слишком молоды чтобы спать по ночам и сейчас
+ /// самое время для приключений! Дамы собрали вещи, уложили спать БиМО и
+ /// отправились из другой воображаемой Москвы в другой воображаемый
+ /// Лондон верхом на лорде Монохроме. Сколько минут они провели в
+ /// путешествии, если Москву они покинули 28.03.2010 в 03:15 по местному
+ /// времени, а в Лондон прибыли в 28.03.2010 в 01:15 по местному?
///
public static int GetGenderSwappedAdventureTimeDurationInMinutes_ver0_Dumb()
{
@@ -165,7 +192,10 @@ public static int GetGenderSwappedAdventureTimeDurationInMinutes_ver0_Dumb()
- 2010, 3, 28, 3, 15, 0
- 2010, 3, 28, 1, 15, 0
*/
- throw new NotImplementedException();
+ var london_t = new DateTimeOffset(2010, 3, 28, 1, 15, 0, TimeSpan.FromHours(0));
+ var moscow_t = new DateTimeOffset(2010, 3, 28, 3, 15, 0, TimeSpan.FromHours(3));
+
+ return Convert.ToInt32(Math.Abs((moscow_t - london_t).TotalMinutes));
}
///
@@ -174,13 +204,22 @@ public static int GetGenderSwappedAdventureTimeDurationInMinutes_ver0_Dumb()
public static int GetAdventureTimeDurationInMinutes_ver1_FeelsSmarter()
{
/*
- Глава вторая, в которой оказывается, что в некоторых странах принята такая штука как летнее время (не совсем то, про которое поет Лана Дель Рей).
-
- Внимательный читатель мог усомниться в данных мной часовых поясах и их смещении относительно UTC и был бы прав.
- На самом деле смещения таковы: Лондон +1 (BST - British Summer Time), Москва +4 (MSD - Moscow Daylight Time).
- Давай теперь учтем правильное смещение. Я понимаю, что это очевидно, что результат не изменится, но тебе же не сложно скопипастить и просто поменять смещения?
+ Глава вторая, в которой оказывается, что в некоторых странах
+ принята такая штука как летнее время (не совсем то, про которое
+ поет Лана Дель Рей).
+
+ Внимательный читатель мог усомниться в данных мной часовых
+ поясах и их смещении относительно UTC и был бы прав. На самом
+ деле смещения таковы: Лондон +1 (BST - British Summer Time),
+ Москва +4 (MSD - Moscow Daylight Time). Давай теперь учтем
+ правильное смещение. Я понимаю, что это очевидно, что результат
+ не изменится, но тебе же не сложно скопипастить и просто
+ поменять смещения?
*/
- throw new NotImplementedException();
+ var london_t = new DateTimeOffset(2010, 3, 28, 1, 15, 0, TimeSpan.FromHours(1));
+ var moscow_t = new DateTimeOffset(2010, 3, 28, 3, 15, 0, TimeSpan.FromHours(4));
+
+ return Convert.ToInt32(Math.Abs((moscow_t - london_t).TotalMinutes));
}
// GetGenderSwappedAdventureTimeDurationInMinutes_ver1_FeelsSmarter опустим, там то же самое
@@ -191,21 +230,39 @@ public static int GetAdventureTimeDurationInMinutes_ver1_FeelsSmarter()
public static int GetAdventureTimeDurationInMinutes_ver2_FeelsLikeRocketScience()
{
/*
- Глава третья и последняя, в которой внезапно оказывается, что Финн и Фионна находятся в суперпозиции и существуют в виде гендерно нейтрального сверхчеловека, который и путешествовал из Москвы в Лондон.
-
- Дело в том, что перевод на летнее время в 2010м году в Москве произошел в 02:00 (стрелки часов перевели на час вперед), а в Лондоне - в 01:00.
- Таким образом в Москве не было 02:15 - однако можно, например, считать, что этому времени соответствует 03:15. Ну а в Лондоне 01:15 это на самом деле 02:15.
- Только как это обработать в рамках класса DateTimeOffset? Да, для конкретного примера мы могли бы сами ручками "перевести стрелки" и поставить правильное время, но что делать в общем случае?
- Тут придется воспользоваться знанием о часовых поясах. Их есть у .Net.
-
- Дабы ты не мучился[-ась], роя в недрах msdn и stackoverflow в поисках ответа (в конце концов, когда тебе это в жизни действительно понадобится),
- ниже ты найдешь готовый метод GetZonedTime. Просто посмотри на него (можешь даже посмотреть методы и свойства типа TimeZoneInfo, если интересно) и воспользуйся им для вычисления правильного времени
- "отбытия" и "прибытия" наших героев. Затем посчитай длительность путешествия. Также даны правильные идентификаторы зон.
+ Глава третья и последняя, в которой внезапно оказывается, что
+ Финн и Фионна находятся в суперпозиции и существуют в виде
+ гендерно нейтрального сверхчеловека, который и путешествовал из
+ Москвы в Лондон.
+
+ Дело в том, что перевод на летнее время в 2010м году в Москве
+ произошел в 02:00 (стрелки часов перевели на час вперед), а в
+ Лондоне - в 01:00. Таким образом в Москве не было 02:15 -
+ однако можно, например, считать, что этому времени соответствует
+ 03:15. Ну а в Лондоне 01:15 это на самом деле 02:15. Только как
+ это обработать в рамках класса DateTimeOffset? Да, для
+ конкретного примера мы могли бы сами ручками "перевести стрелки"
+ и поставить правильное время, но что делать в общем случае? Тут
+ придется воспользоваться знанием о часовых поясах. Их есть у
+ .Net.
+
+ Дабы ты не мучился[-ась], роя в недрах msdn и stackoverflow в
+ поисках ответа (в конце концов, когда тебе это в жизни
+ действительно понадобится), ниже ты найдешь готовый метод
+ GetZonedTime. Просто посмотри на него (можешь даже посмотреть
+ методы и свойства типа TimeZoneInfo, если интересно) и
+ воспользуйся им для вычисления правильного времени "отбытия" и
+ "прибытия" наших героев. Затем посчитай длительность
+ путешествия. Также даны правильные идентификаторы зон.
*/
const string moscowZoneId = "Russian Standard Time";
const string londonZoneId = "GMT Standard Time";
- throw new NotImplementedException();
+ var london_t = new DateTimeOffset(2010, 3, 28, 1, 15, 0, TimeSpan.FromHours(1));
+ var moscow_t = new DateTimeOffset(2010, 3, 28, 3, 15, 0, TimeSpan.FromHours(4));
+
+ return Math.Abs(Convert.ToInt32(GetZonedTime(moscow_t.LocalDateTime, moscowZoneId) -
+ GetZonedTime(london_t.LocalDateTime, londonZoneId)));
}
///
@@ -214,11 +271,18 @@ public static int GetAdventureTimeDurationInMinutes_ver2_FeelsLikeRocketScience(
public static int GetGenderSwappedAdventureTimeDurationInMinutes_ver2_FeelsLikeRocketScience()
{
/*
- Реши по аналогии с предыдущим методом и проверь, что оба метода действительно возвращают одно и то же время (и что оно правильное).
+ Реши по аналогии с предыдущим методом и проверь, что оба метода
+ действительно возвращают одно и то же время (и что оно
+ правильное).
*/
const string moscowZoneId = "Russian Standard Time";
const string londonZoneId = "GMT Standard Time";
- throw new NotImplementedException();
+
+ var london_t = new DateTimeOffset(2010, 3, 28, 1, 15, 0, TimeSpan.FromHours(0));
+ var moscow_t = new DateTimeOffset(2010, 3, 28, 3, 15, 0, TimeSpan.FromHours(3));
+
+ return Convert.ToInt32(GetZonedTime(moscow_t.LocalDateTime, moscowZoneId) -
+ GetZonedTime(london_t.LocalDateTime, londonZoneId));
}
private static DateTimeOffset GetZonedTime(DateTime localTime, string timeZoneId)
@@ -232,7 +296,9 @@ private static DateTimeOffset GetZonedTime(DateTime localTime, string timeZoneId
Console.WriteLine($"{localTime}: invalid = {isInvalid}; daylight = {isDaylightSaving}; ambigous = {isAmbiguous}");
- // несмотря на то, что DateTimeOffset хранит локальное время + смещение, в действительности здесь мы вычисляем правильное абсолютное значение (время UTC)
+ // несмотря на то, что DateTimeOffset хранит локальное время +
+ // смещение, в действительности здесь мы вычисляем правильное
+ // абсолютное значение (время UTC)
return new DateTimeOffset(localTime, timeZone.GetUtcOffset(localTime));
}
@@ -247,12 +313,18 @@ public static int GetAdventureTimeDurationInMinutes_ver3_NodaTime()
const string londonTimeZoneId = "Europe/London";
const string moscowTimeZoneId = "Europe/Moscow";
- // Тип LocalDateTime не хранит информации о том, где "наблюдается" это время, но явно говорит, что данное время нужно трактовать как есть и никаких неявных преобразований не делать.
- // Более того, его апи не позволяет тебе сделать что-то неявно или трактовать это время как-то иначе. Например, его невозможно превратить в абсолютное время UTC (в Noda Time ему отвечает тип Instant)
+ // Тип LocalDateTime не хранит информации о том, где "наблюдается"
+ // это время, но явно говорит, что данное время нужно трактовать как
+ // есть и никаких неявных преобразований не делать. Более того, его
+ // апи не позволяет тебе сделать что-то неявно или трактовать это
+ // время как-то иначе. Например, его невозможно превратить в
+ // абсолютное время UTC (в Noda Time ему отвечает тип Instant)
var from = new LocalDateTime(2010, 3, 28, 2, 15, 0);
var to = new LocalDateTime(2010, 3, 28, 2, 15, 0);
- //Тип ZonedDateTime - это ровным счетом LocalDateTime + DateTimeZone (локальное время + часовой пояс). Вот из него абсолютное время уже можно получить (информации достаточно).
+ // Тип ZonedDateTime - это ровным счетом LocalDateTime + DateTimeZone
+ // (локальное время + часовой пояс). Вот из него абсолютное время уже
+ // можно получить (информации достаточно).
var fromMoscowZoned = GetZonedTime(from, moscowTimeZoneId);
var toLondonZoned = GetZonedTime(to, londonTimeZoneId);
return (int) (toLondonZoned - fromMoscowZoned).TotalMinutes;
@@ -260,10 +332,15 @@ public static int GetAdventureTimeDurationInMinutes_ver3_NodaTime()
private static ZonedDateTime GetZonedTime(LocalDateTime localTime, string timeZoneId)
{
- // здесь используется не windows-specific словарь идентификаторов, а более "принятый" сообществом
+ // здесь используется не windows-specific словарь идентификаторов, а
+ // более "принятый" сообществом
var timeZone = TzdbDateTimeZoneSource.Default.ForId(timeZoneId);
- // обрати внимание, есть два метода, превращающих локальное время + часовой пояс в ZonedDateTime: InZoneLeniently и InZoneStrictly. Первый не ругается на сомнительное локальное время, второй - бросает исключение. Для наглядности конкретно этого примера я использовал "снисходительный" вариант.
+ // обрати внимание, есть два метода, превращающих локальное время +
+ // часовой пояс в ZonedDateTime: InZoneLeniently и InZoneStrictly.
+ // Первый не ругается на сомнительное локальное время, второй -
+ // бросает исключение. Для наглядности конкретно этого примера я
+ // использовал "снисходительный" вариант.
return localTime.InZoneLeniently(timeZone);
}
@@ -277,7 +354,9 @@ private static ZonedDateTime GetZonedTime(LocalDateTime localTime, string timeZo
/// True - если родились в один день, иначе - false.
internal static bool AreEqualBirthdays(DateTime person1Birthday, DateTime person2Birthday)
{
- throw new NotImplementedException();
+ return person1Birthday.Year == person2Birthday.Year &&
+ person1Birthday.Month == person2Birthday.Month &&
+ person1Birthday.Day == person2Birthday.Day;
}
}
}
diff --git a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector.Tests/BoringVector.Tests.csproj b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector.Tests/BoringVector.Tests.csproj
new file mode 100644
index 00000000..122b68e1
--- /dev/null
+++ b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector.Tests/BoringVector.Tests.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net5.0
+
+ false
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector.Tests/VectorTest.cs b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector.Tests/VectorTest.cs
new file mode 100644
index 00000000..f495c69f
--- /dev/null
+++ b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector.Tests/VectorTest.cs
@@ -0,0 +1,50 @@
+using System;
+using Xunit;
+
+namespace BoringVector.Tests
+{
+ public class VectorTest
+ {
+ [Fact]
+ public void Test_SquareLength()
+ {
+ Vector v = new Vector(3, 4);
+ Assert.Equal(25, v.SquareLength());
+ }
+ [Theory]
+ [InlineData(1, 5)]
+ [InlineData(0, 0)]
+ [InlineData(-1, 1)]
+ public void Test_Add(double x, double y)
+ {
+ Vector v = new Vector(3, 4);
+ Vector u = new Vector(x, y);
+ v.Add(u);
+ Assert.Equal(new Vector(3+x, 4+y).ToString(), v.ToString());
+ }
+ [Theory]
+ [InlineData(5)]
+ [InlineData(0)]
+ [InlineData(-2)]
+ public void Test_Scale(double scale)
+ {
+ Vector v = new Vector(3, 4);
+ v.Scale(scale);
+ Assert.Equal(new Vector(3*scale, 4*scale).ToString(), v.ToString());
+ }
+ [Fact]
+ public void Test_DotProduct()
+ {
+ Vector v = new Vector(3, 4);
+ Vector u = new Vector(1, -1);
+ Assert.Equal(-1, v.DotProduct(u));
+ }
+ [Fact]
+ public void Test_CrossProduct()
+ {
+ Vector v = new Vector(3, 4);
+ Vector u = new Vector(4, -3);
+ Assert.Equal(-25, v.CrossProduct(u));
+ }
+ }
+}
diff --git a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/BoringVector.csproj b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/BoringVector.csproj
index ce1697ae..680291a4 100644
--- a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/BoringVector.csproj
+++ b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/BoringVector.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp2.0
+ netcoreapp5.0
diff --git a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/Program.cs b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/Program.cs
index f5873162..5c94dfd1 100644
--- a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/Program.cs
+++ b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/Program.cs
@@ -1,4 +1,7 @@
using System;
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("BoringVector.Tests")]
namespace BoringVector
{
diff --git a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/Vector.cs b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/Vector.cs
index bb9db585..dd2c1745 100644
--- a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/Vector.cs
+++ b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/Vector.cs
@@ -1,5 +1,6 @@
using System;
+
namespace BoringVector
{
#region 1. Структура Vector
@@ -8,12 +9,21 @@ namespace BoringVector
Реализуй структуру Vector - см. комментарии внутри нее.
*/
- internal struct Vector
+ ///
+ /// Класс, реализующий двумерный вектор.
+ ///
+ internal class Vector
{
/*
Vector задается парой вещественных координат X и Y.
*/
-
+ protected double x;
+ protected double y;
+ public Vector(double x, double y)
+ {
+ this.x = x;
+ this.y = y;
+ }
/*
На месте заглушек добавь реализацию базовых методов вектора:
@@ -24,30 +34,54 @@ internal struct Vector
- векторное произведение (= площадь параллелограмма)
*/
+ ///
+ /// Квадрат длины вектора.
+ ///
public double SquareLength()
{
- throw new NotImplementedException();
+ return x*x + y*y;
}
+ ///
+ /// Сложение векторов (in-place).
+ ///
+ /// Вектор, который будет прибавлен к текущему.
public Vector Add(Vector v)
{
- throw new NotImplementedException();
+ x += v.x;
+ y += v.y;
+ return this;
}
+ ///
+ /// Умножение на скаляр (in-place).
+ ///
+ /// Коэффициент.
public Vector Scale(double k)
{
- throw new NotImplementedException();
+ x *= k;
+ y *= k;
+ return this;
}
+ ///
+ /// Скалярное произведение векторов.
+ ///
+ /// Вектор, с которым берётся скалярное произведение.
public double DotProduct(Vector v)
{
- throw new NotImplementedException();
+ return x*v.x + y*v.y;
}
+ ///
+ /// Векторное произведение векторов.
+ ///
+ /// Вектор, с которым берётся векторное произведение.
public double CrossProduct(Vector v)
{
- throw new NotImplementedException();
+ return x*v.y - y*v.x;
}
/*
Переопредели ниже метод ToString - пусть выводит (X; Y)
*/
+ public override string ToString() => String.Format("({0}; {1})", x, y);
#region operators
@@ -57,6 +91,10 @@ public double CrossProduct(Vector v)
- k * v, v * k, v / k
- +v, -v
*/
+ public static Vector operator+(Vector v) => v;
+ public static Vector operator-(Vector v) => new Vector(-v.x, -v.y);
+ public static Vector operator+(Vector v, Vector u) => new Vector(v.x + u.x, v.y + u.y);
+ public static Vector operator-(Vector v, Vector u) => new Vector(v.x - u.x, v.y - u.y);
#endregion
}
@@ -72,30 +110,43 @@ public double CrossProduct(Vector v)
#region 3. Комментарии
/*
- Прости, я соврал. К сожалению, пока ты делал(-а) вторую часть, человек, который понимал, о чем идет речь в первой части,
- успел съездить в путешествие под названием "во все тяжкие" и теперь проходит курс реабилитации. А те, кто был хотя бы примерно в теме,
- внезапно лишились рассудка.
- Тут еще из будущего сообщили, что тебе дали задачу поинтереснее, и ты наотрез отказался(-ась) сотрудничать в передаче знаний.
- Ну а вместо тебя на эту задачу взяли пушистого котика, который отказывается разбираться в коде и уж тем более его писать - говорит у него лапки.
-
- В такой ситуации единственно возможное решение задачи - оставить после себя комментарии. Такие, чтобы даже котику было понятно!
- В общем, впереди неприятная для многих часть - необходимо добавить комментарии ко всем введенным типам, методам и свойствам :)
-
-
+ Прости, я соврал. К сожалению, пока ты делал(-а) вторую часть, человек,
+ который понимал, о чем идет речь в первой части, успел съездить в
+ путешествие под названием "во все тяжкие" и теперь проходит курс
+ реабилитации. А те, кто был хотя бы примерно в теме, внезапно лишились
+ рассудка. Тут еще из будущего сообщили, что тебе дали задачу
+ поинтереснее, и ты наотрез отказался(-ась) сотрудничать в передаче
+ знаний. Ну а вместо тебя на эту задачу взяли пушистого котика, который
+ отказывается разбираться в коде и уж тем более его писать - говорит у
+ него лапки.
+
+ В такой ситуации единственно возможное решение задачи - оставить после
+ себя комментарии. Такие, чтобы даже котику было понятно! В общем,
+ впереди неприятная для многих часть - необходимо добавить комментарии ко
+ всем введенным типам, методам и свойствам :)
+
+
_Хозяйке на заметку_
-
- Для многих это действительно довольно неприятная и скучнейшая часть - писать комментарии к коду. Проблема в том,
- что это еще и не так просто как кажется на первый взгляд. Очень желательно выдерживать единый стиль, писать по существу,
- писать не "для текущего разобравшегося в проблеме и предметной области себя", а для "того парня", "себя через год". Ну или пушистого котика.
-
- Есть и хорошее в этом деле. Комментирование кода очень похоже на написание автотестов - оно позволяет взглянуть
- на задачу и ее решение немного с другой стороны. Например, если слова не вяжутся, и не получается написать простое и короткое
- описание к методу или типу - это хороший сигнал, что он "с душком", и его стоит переделать/отрефакторить.
- Я лично чаще пишу комментарии ближе к концу работы над задачей. Это позволяет мне еще раз просмотреть весь написанный код под
- слегка иным углом обзора и самому сделать первичный code review.
-
-
- Ты мог(-ла) заметить, что в предыдущих заданиях для комментариев я использовал немного необычный синтаксис:
+
+ Для многих это действительно довольно неприятная и скучнейшая часть -
+ писать комментарии к коду. Проблема в том, что это еще и не так просто
+ как кажется на первый взгляд. Очень желательно выдерживать единый стиль,
+ писать по существу, писать не "для текущего разобравшегося в проблеме и
+ предметной области себя", а для "того парня", "себя через год". Ну или
+ пушистого котика.
+
+ Есть и хорошее в этом деле. Комментирование кода очень похоже на
+ написание автотестов - оно позволяет взглянуть на задачу и ее решение
+ немного с другой стороны. Например, если слова не вяжутся, и не
+ получается написать простое и короткое описание к методу или типу - это
+ хороший сигнал, что он "с душком", и его стоит переделать/отрефакторить.
+ Я лично чаще пишу комментарии ближе к концу работы над задачей. Это
+ позволяет мне еще раз просмотреть весь написанный код под слегка иным
+ углом обзора и самому сделать первичный code review.
+
+
+ Ты мог(-ла) заметить, что в предыдущих заданиях для комментариев я
+ использовал немного необычный синтаксис:
///
/// Возвращает объект с заданными временем и значением .
@@ -104,33 +155,52 @@ public double CrossProduct(Vector v)
/// Значение , задающий соответствующее свойство возвращаемого объекта.
/// Объект с заданными временем и значением .
- Такой блок является комментарием, т.к. каждая строка начинается с //, но имеет свою внутреннюю структуру и синтаксис.
- Это так называемые Xml documentation comments. Их поддерживает сам компилятор. Они позволяют писать чуть более умные и продвинутые комментарии к сущностям,
- а потом, например, автоматически генерировать по ним красивую документацию.
- Правилом хорошего тона считается писать комментарии к методам, классам и другим сущностям, используя данный синтаксис - так ты и комментируешь их, и документируешь.
- Внутри методов он не поддерживается, поэтому там только обычные (// или /*).
-
- Ниже приведены примеры простейших комментариев. Если наведете мышкой на название метода DoNothing, увидишь,
- что студия отображает комментарий в подписи (в других IDE из коробки без плагинов это вряд ли будет работать).
- Если навести на аргумент метода DoNothing - something, увидишь комментарий к нему.
- Наведи теперь на NotImplementedException() - комментарий во всплывашке сделан с помощью такого же синтаксиса.
- Код .Net Framework задокументирован именно таким синтаксисом.
-
- Чтобы создать такой комментарий, не нужно писать разметку целиком самому. Если ввести ///, студия сама создаст нужные блоки.
+ Такой блок является комментарием, т.к. каждая строка начинается с //, но
+ имеет свою внутреннюю структуру и синтаксис. Это так называемые Xml
+ documentation comments. Их поддерживает сам компилятор. Они позволяют
+ писать чуть более умные и продвинутые комментарии к сущностям, а потом,
+ например, автоматически генерировать по ним красивую документацию.
+ Правилом хорошего тона считается писать комментарии к методам, классам и
+ другим сущностям, используя данный синтаксис - так ты и комментируешь
+ их, и документируешь. Внутри методов он не поддерживается, поэтому там
+ только обычные (// или /*).
+
+ Ниже приведены примеры простейших комментариев. Если наведете мышкой на
+ название метода DoNothing, увидишь, что студия отображает комментарий в
+ подписи (в других IDE из коробки без плагинов это вряд ли будет
+ работать). Если навести на аргумент метода DoNothing - something,
+ увидишь комментарий к нему. Наведи теперь на NotImplementedException()
+ - комментарий во всплывашке сделан с помощью такого же синтаксиса. Код
+ .Net Framework задокументирован именно таким синтаксисом.
+
+ Чтобы создать такой комментарий, не нужно писать разметку целиком
+ самому. Если ввести ///, студия сама создаст нужные блоки.
Плюсы Xml documentation comments:
- - блок комментариев имеет четкую предзаданную структуру, что дает возможность воспользоваться различными инструментариями
- автогенерации документации (и создавать, например, документацию вроде MSDN).
- В последней лабе мы, надеюсь, научимся генерировать для web api страничку с документацией к нашему апи и увидим, что генератор в том числе
- может для каждого метода апи создавать формочку для его проверки и ручного тестирования.
- - IDE может показывать ее в чуть более удобном виде, нежели если бы ты писал(-а) их обычным способом.
- Например, показывать во всплывающей подсказке только самое необходимое короткое описание, а длинное с пояснениями и примерами кода не показывать.
- - все названия типов, методов и свойств можно задавать в виде ссылки, например, , и это
- будет строгая, проверяемая компилятором, ссылка. Ты, думаю, помнишь, что компилятор как сервис в частности предоставляет разные возможности рефакторинга,
- например, переименование. Так вот, если воспользоваться им и переименовать метод DotProduct у структуры Vector, то
- переименуется и текст в ссылке. Если же ссылка указывает на невалидную сущность, то студия явно даст понять: "Cannot resolve symbol 'blah-blah-blah'".
- Я лично не проверял, но по идее можно даже сделать так, чтобы в таком случае была ошибка компиляции. В генерируемой же документации эти ссылки будут
- заменены на реальные ссылки на страницы описания соответствующей сущности, что довольно удобно для навигации по ней.
+ - блок комментариев имеет четкую предзаданную структуру, что дает
+ возможность воспользоваться различными инструментариями
+ автогенерации документации (и создавать, например, документацию
+ вроде MSDN). В последней лабе мы, надеюсь, научимся генерировать
+ для web api страничку с документацией к нашему апи и увидим, что
+ генератор в том числе может для каждого метода апи создавать
+ формочку для его проверки и ручного тестирования.
+ - IDE может показывать ее в чуть более удобном виде, нежели если бы
+ ты писал(-а) их обычным способом. Например, показывать во
+ всплывающей подсказке только самое необходимое короткое описание, а
+ длинное с пояснениями и примерами кода не показывать.
+ - все названия типов, методов и свойств можно задавать в виде
+ ссылки, например, , и это будет
+ строгая, проверяемая компилятором, ссылка. Ты, думаю, помнишь, что
+ компилятор как сервис в частности предоставляет разные возможности
+ рефакторинга, например, переименование. Так вот, если
+ воспользоваться им и переименовать метод DotProduct у структуры
+ Vector, то переименуется и текст в ссылке. Если же ссылка указывает
+ на невалидную сущность, то студия явно даст понять: "Cannot resolve
+ symbol 'blah-blah-blah'". Я лично не проверял, но по идее можно
+ даже сделать так, чтобы в таком случае была ошибка компиляции. В
+ генерируемой же документации эти ссылки будут заменены на реальные
+ ссылки на страницы описания соответствующей сущности, что довольно
+ удобно для навигации по ней.
*/
@@ -160,28 +230,36 @@ public static void ThrowNotImplementedException()
#region 4. Тесты
/*
- Прости еще раз, но на этом твои мучения еще не кончаются. Проблема в том, что у котика все еще лапки, поэтому очень важно
- покрыть тестами хотя бы базовые методы структуры Vector:
+ Прости еще раз, но на этом твои мучения еще не кончаются. Проблема в
+ том, что у котика все еще лапки, поэтому очень важно покрыть тестами
+ хотя бы базовые методы структуры Vector:
SquareLength
Add
Scale
DotProduct
CrossProduct
- Для этого создай в этом же солюшене (если ты не в студии, то можешь и не в солюшене) проект BoringVector.Tests,
- который будет содержать класс с набором тестов. Используй Xunit (в принципе можешь воспользоваться и другим фреймворком для тестирования).
+ Для этого создай в этом же солюшене (если ты не в студии, то можешь и не
+ в солюшене) проект BoringVector.Tests, который будет содержать класс с
+ набором тестов. Используй Xunit (в принципе можешь воспользоваться и
+ другим фреймворком для тестирования).
- Особо не заморачивайся с тем, чтобы оттестировать все возможные специальные случаи - в данном задании важно, чтобы
- ты просто разобрался(-ась), как писать автотесты и как их запускать. Это задание НЕ на то, как писать хорошие тесты.
+ Особо не заморачивайся с тем, чтобы оттестировать все возможные
+ специальные случаи - в данном задании важно, чтобы ты просто
+ разобрался(-ась), как писать автотесты и как их запускать. Это задание
+ НЕ на то, как писать хорошие тесты.
- Примечание: структура Vector описана как internal структура, поэтому по умолчанию сборке BoringVector.Tests она не видна.
- Чтобы она была видна, существует специальная директива компилятору:
+ Примечание: структура Vector описана как internal структура, поэтому по
+ умолчанию сборке BoringVector.Tests она не видна. Чтобы она была видна,
+ существует специальная директива компилятору:
[assembly: InternalsVisibleTo("XXX")]
, где XXX - название проекта, которому ты хочешь сделать видимыми свои internal'ы.
- Можешь посмотреть, в задании [01-primitive-types] эта директива есть в файле Program.cs проекта Numbers.
+ Можешь посмотреть, в задании [01-primitive-types] эта директива есть в
+ файле Program.cs проекта Numbers.
- Итак, создай проект с тестами и добейся того, чтобы базовые методы структуры Vector их проходили.
+ Итак, создай проект с тестами и добейся того, чтобы базовые методы
+ структуры Vector их проходили.
*/
#endregion
diff --git a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/VectorExtensions.cs b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/VectorExtensions.cs
index 525be40f..07b4e7c7 100644
--- a/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/VectorExtensions.cs
+++ b/course-2021-1/exercises/03-boring-vector/BoringVector/BoringVector/VectorExtensions.cs
@@ -1,10 +1,58 @@
-namespace BoringVector
+using System;
+
+namespace BoringVector
{
+ enum VectorRelation
+ {
+ General,
+ Parallel,
+ Orthogonal
+ }
/*
Здесь тебе нужно написать класс с методами-расширениями структуры Vector:
- - IsZero: проверяет, является ли вектор нулевым, т.е. его координаты близки к нулю (в эпсилон окрестности). За эпсилон здесь и далее берем 1e-6.
+ - IsZero: проверяет, является ли вектор нулевым, т.е. его координаты
+ близки к нулю (в эпсилон окрестности). За эпсилон здесь и далее
+ берем 1e-6.
- Normalize: нормализует вектор
- - GetAngleBetween: возвращает угол между двумя векторами в радианах. Примечание: нулевой вектор сонаправлен любому другому.
- - GetRelation: возвращает значение перечесления VectorRelation(General, Parallel, Orthogonal) - отношение между двумя векторами("общий случай", параллельны, перпендикулярны). Перечисление задавать тоже тебе)
+ - GetAngleBetween: возвращает угол между двумя векторами в радианах.
+ Примечание: нулевой вектор сонаправлен любому другому.
+ - GetRelation: возвращает значение перечесления
+ VectorRelation(General, Parallel, Orthogonal) - отношение между
+ двумя векторами("общий случай", параллельны, перпендикулярны).
+ Перечисление задавать тоже тебе)
*/
+ internal class ExtendedVector : Vector
+ {
+ const double eps = 0.000001;
+ public ExtendedVector(double x, double y) : base(x, y) {}
+ public bool IsZero()
+ {
+ return Math.Abs(x) < eps && Math.Abs(y) < eps;
+ }
+
+ public ExtendedVector Normalize()
+ {
+ double len = Math.Sqrt(SquareLength());
+ x /= len;
+ y /= len;
+ return this;
+ }
+
+ static public double GetAngleBetween(ExtendedVector v, ExtendedVector u)
+ {
+ if (v.IsZero() || u.IsZero()) {
+ return 0;
+ }
+ return Math.Acos(v.Normalize().DotProduct(u.Normalize()));
+ }
+ static public VectorRelation GetRelation(ExtendedVector v, ExtendedVector u)
+ {
+ if (v.CrossProduct(u) == 0) {
+ return VectorRelation.Parallel;
+ } else if (v.DotProduct(u) == 0) {
+ return VectorRelation.Orthogonal;
+ }
+ return VectorRelation.General;
+ }
+ }
}