|
|
@@ -1,8 +1,8 @@
|
|
|
-# Chapter 8. Generics
|
|
|
+# Глава 8. Обобщения
|
|
|
|
|
|
-The items iterator that we have just seen only works on `seq[Point]`. However, the code does not use any features of `Point`, it doesn’t access `point.x`, for example. We really want to iterate over every `seq[T]` where `T` can be any type.
|
|
|
+Итератор `items`, который только что был рассмотрен, работает только с `seq[Point]`. Однако в коде не используются никакие возможности `Point`, например он не обращается к `point.x`. Бывает нужно перебрать все `seq[T]`, где `T` может быть любого типа.
|
|
|
|
|
|
-**Nim** supports such type variables via generics:
|
|
|
+**Nim** поддерживает такие переменные типов с помощью _обобщений_:
|
|
|
|
|
|
```nim
|
|
|
iterator items[T](s: seq[T]): T = # 1
|
|
|
@@ -12,35 +12,31 @@ iterator items[T](s: seq[T]): T = # 1
|
|
|
for x in items(@["1", "2", "3"]): discard # 3
|
|
|
```
|
|
|
|
|
|
-- 1 The items iterator works for any type `seq[T]`. It produces values of type `T`.
|
|
|
-- 2 `@[1, 2, 3]` has type `seq[int]`. When items is called its type variable `T` is inferred to be `int`.
|
|
|
-- 3 `@["1", "2", "3"]` has type `seq[string]`. When items is called its type variable `T` is inferred to be string.
|
|
|
+- 1 Итератор `items` работает с любым типом `seq[T]`. Он возвращает значения типа `T`.
|
|
|
+- 2 `@[1, 2, 3]` имеет тип `seq[int]`. При вызове `items` его переменная типа `T` определяется как `int`.
|
|
|
+- 3 `@["1", "2", "3"]` имеет тип `seq[string]`. При вызове `items` его переменная типа `T` определяется как `string`.
|
|
|
|
|
|
-**Nim** uses specialization for its generics. Every new concrete type like `int` or
|
|
|
-`string` produces specialized code, there is no runtime overhead.
|
|
|
+**Nim** использует специализацию для обобщений. Каждый новый конкретный тип, такой как `int` или `string`, генерирует специализированный код, что не требует дополнительных затрат во время выполнения.
|
|
|
|
|
|
-Not only `procs` and iterators but also types can be generic:
|
|
|
+Обобщенными могут быть не только `procs` и итераторы, но и типы:
|
|
|
|
|
|
```nim
|
|
|
type
|
|
|
- Point[T] = object # 1
|
|
|
- x, y: T # 2
|
|
|
- var p: Point[float] # 3
|
|
|
- p = Point[float](x: 1.0, y: 3.0) # 4
|
|
|
+ Point[T] = object # 1
|
|
|
+ x, y: T # 2
|
|
|
+
|
|
|
+var p: Point[float] # 3
|
|
|
+p = Point[float](x: 1.0, y: 3.0) # 4
|
|
|
```
|
|
|
|
|
|
-- 1 The `Point` type is parametrized by a type variable `T`.
|
|
|
-- 2 `T` is used to declare the fields `x` and `y`.
|
|
|
-- 3 A variable of name `p` is declared that is of type `Point[float]`
|
|
|
-- 4 Object construction of `Point` also requires an explicit type; in this case `float`.
|
|
|
+- 1 Тип `Point` параметризуется переменной типа `T`.
|
|
|
+- 2 `T` используется для объявления полей `x` и `y`.
|
|
|
+- 3 Объявлена переменная с именем `p`, имеющая тип `Point[float]`
|
|
|
+- 4 Для создания объекта `Point` также требуется явный тип; в данном случае `float`.
|
|
|
|
|
|
-Unfortunately type inference does not work for object construction, `Point(x:
|
|
|
-1.0, y: 3.0)` is not allowed. This restriction will probably be removed in the
|
|
|
-future.
|
|
|
+К сожалению, вывод типов не работает при создании объектов, `Point(x: 1.0, y: 3.0)` не допускается без параметризации явным типом. Вероятно, в будущем это ограничение будет снято.
|
|
|
|
|
|
-If a type is parametrized by a type variable `T` operations on it usually have to
|
|
|
-be parametrized too. For example, our `drawHorizontalLine` proc would
|
|
|
-become:
|
|
|
+Если тип параметризован переменной типа `T`, то операции с ним обычно тоже должны быть параметризованы. Например, процедура `drawHorizontalLine` стала бы такой:
|
|
|
|
|
|
```nim
|
|
|
proc drawHorizontalLine[T](a, b: Point[T]) =
|
|
|
@@ -51,16 +47,14 @@ proc drawHorizontalLine[T](a, b: Point[T]) =
|
|
|
putPixel(x, a.y)
|
|
|
```
|
|
|
|
|
|
-`drawHorizontalLine` takes two parameters of the same type `Point[T]`. In other
|
|
|
-words, a call like `drawHorizontalLine(Point[float](x: 2.0, y: 3.0),
|
|
|
-Point[int](x: 2, y: 3))` would be rejected because one `T` cannot be both
|
|
|
-`float` and `int` at the same time.
|
|
|
+`drawHorizontalLine` принимает два параметра одного типа `Point[T]`. Другими словами, вызов вида `drawHorizontalLine(Point[float](x: 2.0, y: 3.0), Point[int](x: 2, y: 3))` будет отклонен, потому что один `T` не может быть одновременно `float` и `int`.
|
|
|
+
|
|
|
+Мы можем использовать переменные разных типов, чтобы обеспечить работу с несколькими параметрическими типами одновременно
|
|
|
|
|
|
-We can use different type variables to allow for
|
|
|
+`drawHorizontalLine(Point[float](x: 2.0, y: 3.0), Point[int](x: 2, y: 3))`:
|
|
|
|
|
|
```nim
|
|
|
-drawHorizontalLine(Point[float](x: 2.0, y: 3.0), Point[int](x: 2, y: 3)):
|
|
|
- proc drawHorizontalLine[T, U](a: Point[T]; b: Point[U]) =
|
|
|
+proc drawHorizontalLine[T, U](a: Point[T]; b: Point[U]) =
|
|
|
if b.x < a.x:
|
|
|
drawHorizontalLine(b, a)
|
|
|
else:
|
|
|
@@ -68,8 +62,7 @@ drawHorizontalLine(Point[float](x: 2.0, y: 3.0), Point[int](x: 2, y: 3)):
|
|
|
putPixel(x, a.y)
|
|
|
```
|
|
|
|
|
|
-This assumes that we have an iterator `..` that can handle mixed types. We
|
|
|
-could provide such an iterator like this:
|
|
|
+то предполагает, что у нас есть итератор `..`, который может работать со смешанными типами. Например, можно реализовать такой итератор следующим образом:
|
|
|
|
|
|
```nim
|
|
|
iterator `..`[T, U](a: T, b: U): U = # 1
|
|
|
@@ -79,17 +72,15 @@ iterator `..`[T, U](a: T, b: U): U = # 1
|
|
|
inc i # 4
|
|
|
```
|
|
|
|
|
|
-- 1 Somewhat arbitrarily we have decided that the produced values are of type `U` and not of `T`.
|
|
|
-- 2 Via `U(a)` we convert the starting value a to type `U`. In **Nim** a type conversion looks like a function call.
|
|
|
-- 3 We assume here that type `U` offers an operator `<=`. This assumption is not written down — generics in **Nim** can be under-specified.
|
|
|
-- 4 We assume here that type `U` offers a suitable operation `inc`.
|
|
|
+- 1 несколько произвольно решено, что создаваемые значения относятся к типу `U`, а не к `T`.
|
|
|
+- 2 С помощью `U(a)` мы преобразуем исходное значение `a` в тип `U`. В **Nim** преобразование типа выглядит как вызов функции.
|
|
|
+- 3 Здесь делается предположение, что тип `U` предлагает оператор `<=`. Это предположение не зафиксировано — обобщения в **Nim** могут быть недостаточно конкретными.
|
|
|
+4 Здесь предположено, что тип `U` поддерживает подходящую операцию `inc`.
|
|
|
|
|
|
-A type variable `T` is usually left under-specified in **Nim**; the requirements are
|
|
|
-only implicit and generic code is only type checked when the generic is
|
|
|
-instantiated:
|
|
|
+Переменная типа `T` обычно не указывается в **Nim**; требования являются только неявными, и тип универсального кода проверяется только при создании экземпляра универсального кода:
|
|
|
|
|
|
```nim
|
|
|
-for x in "a".."b": ... # invalid
|
|
|
-for x in 0 .. 3: ... # valid
|
|
|
-for x in 0 .. 3.0: ... # invalid because float does not have `inc`
|
|
|
+for x in "a".."b": ... # неправильно
|
|
|
+for x in 0 .. 3: ... # правильно
|
|
|
+for x in 0 .. 3.0: ... # неправильные типы: `float` не является `inc`
|
|
|
```
|