Skip to content

Instantly share code, notes, and snippets.

@AnthonyMikh
Last active February 16, 2018 15:10
Show Gist options
  • Save AnthonyMikh/38c6a14280ebf6957bc9e7b1bcd57987 to your computer and use it in GitHub Desktop.
Save AnthonyMikh/38c6a14280ebf6957bc9e7b1bcd57987 to your computer and use it in GitHub Desktop.
Решение задачи №70 от UniLecs
type Coord = i16;
type Point = (Coord, Coord); //Тип входных данных
type VectorCoord = i32;
struct Vector2(VectorCoord, VectorCoord); //Тип вектора на плоскости
impl Vector2 {
//Конструирует вектор от точки from до точки to
fn from_points(from: &Point, to: &Point) -> Self {
Self{
0: (to.0 - from.0).into(),
1: (to.1 - from.1).into(),
}
}
//Считает скалярное произведение векторов
fn scalar_product(&self, rhs: &Self) -> VectorCoord {
let &Self{0: x1, 1: y1} = self;
let &Self{0: x2, 1: y2} = rhs;
x1 * x2 + y1 * y2
}
//Проверяет, являются ли векторы ортогональными
fn is_orthogonal_to(&self, rhs: &Self) -> bool {
self.scalar_product(rhs) == 0
}
}
const ANGLES: usize = 4;
fn count_right_angles(points: &[Point]) -> usize {
assert!(!points.is_empty()); //Проверяем, что массив точек не пуст...
assert_eq!(points.len(), ANGLES); //...и содержит требуемое условием задачи количество точек
//^----- Если эту строчку убрать, то функция будет работать
//с многоугольниками с произвольным числом сторон
//Явная аннотация типа здесь необходима (см. комментарии к гисту)
let from_points_pair: fn(_) -> _ = |(a, b)| Vector2::from_points(a, b);
let sides = points.iter()
.zip(points.iter().cycle().skip(1)) //Образуем пары идущих подряд точек
.map(from_points_pair); //и конвертируем в векторы
sides.clone()
.zip(sides.cycle().skip(1)) //Образуем пары идущих подряд векторов
.map(|(side1, side2)| if side1.is_orthogonal_to(&side2) { 1 } else { 0 })
.sum() //Считаем число пар ортогональных векторов
}
//Тестируем
#[test]
fn do_tests() {
let polygon = [(-1, 0), (-1, 4), (2, 4), (4, 1)];
assert_eq!(count_right_angles(&polygon), 1);
let polygon = [(0, 0), (0, 1), (1, 1), (1, 0)];
assert_eq!(count_right_angles(&polygon), 4);
let polygon = [(-3, -2), (-1, -1), (0, 0), (1, 2)];
assert_eq!(count_right_angles(&polygon), 0);
let polygon = [(8, 1), (12, 1), (12, 4), (11, 3)];
assert_eq!(count_right_angles(&polygon), 1);
let polygon = [(-1, -1), (0, -3), (4, -1), (3, 1)];
assert_eq!(count_right_angles(&polygon), 4);
let polygon = [(-100, -100), (-100, 100), (100, 100), (100, -100)];
assert_eq!(count_right_angles(&polygon), 4);
}
@AnthonyMikh
Copy link
Author

AnthonyMikh commented Feb 13, 2018

@AnthonyMikh
Copy link
Author

По поводу аннотации типа у функции:
Методы zip, map и им подобные у итераторов не выполняют итерацию как таковую, а возвращают адаптеры итераторов, реализующие нужную функциональность, поэтому итерация по коллекции откладывается до вызова метода, поглощающего итератор (такого, как sum или collect). Этим можно воспользоваться, чтобы избежать лишних выделений памяти в куче. Для того, чтобы из sides получить последовательность идущих подряд пар, требуется его клонировать, а сделать это можно только в том случае, если все составляющие итератора можно клонировать (т. е. если для всех составляющих итератора реализован типаж Clone). Метод map принимает на вход значение, для типа которого реализован типаж FnMut. Из встроенных типов Rust он реализован для обычных функций и замыканий. Т. к. замыкание может захватывать окружение, оно в общем случае не может быть склонировано, поэтому вариант кода со строчкой .map(|(a, b)| Vector2::from_points(a, b)) вызывает ошибку компиляции. Обычные функции же, напротив, могут быть склонированы (т. к. на уровне реализации первоклассные функции представлены неизменяемыми указателями на область памяти с кодом). Начиная с версии rustc 1.19 замыкания, не захватывающие окружение, могут быть приведены к типу обычной функции (fn). Для этого им нужно явно приписать тип fn(...) -> .... К счастью, в Rust достаточно развит вывод типов, чтобы конкретные типы аргументов и возвращаемого результата можно было опустить (что и было сделано).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment