Почему функция [-A1, & hellip; + B] не разрешает использование любых супертипов в качестве параметров?

Если вы хотите как можно ближе подойти к синтаксису выражения, попробуйте функцию range из bash-трюков range.bash .

Для Например, все следующие будут делать то же самое, что и echo {1..10}:

source range.bash
one=1
ten=10

range {$one..$ten}
range $one $ten
range {1..$ten}
range {1..10}

Он пытается поддерживать собственный синтаксис bash с максимально возможным количеством «gotchas»: поддерживаются не только переменные, но часто нежелательное поведение недопустимых диапазонов, предоставляемых как строки (например, for i in {1..a}; do echo $i; done), также предотвращается.

Другие ответы будут работать в большинстве случаев, но все они имеют по крайней мере одно из следующих Недостатки:

  • Многие из них используют подоболочки , что может снизить производительность и , возможно, невозможно на некоторых систем.
  • Многие из них полагаются на внешние программы. Даже seq представляет собой двоичный файл, который должен быть установлен для использования, должен быть загружен bash и должен содержать ожидаемую программу, чтобы он работал в этом случае.
  • Решения, которые используют только собственные функции Bash, такие как @ ephemient, не будут работать в алфавитном диапазоне, например {a..z}; расширение скобки будет. Вопрос был о диапазонах номеров , хотя это так.
  • Большинство из них не визуально похожи на синтаксис диапазона расширенного набора {1..10}, поэтому программы, которые используют оба варианта, могут быть немного труднее читать.
  • @ Ответ bobbogo использует некоторый знакомый синтаксис, но делает что-то неожиданное, если переменная $END не является допустимым диапазоном «bookend» для другая сторона диапазона. Если END=a, например, ошибка не будет выполнена, и будет передано дословное значение {1..a}. Это также поведение Bash по умолчанию - это просто неожиданно.

Отказ от ответственности: я являюсь автором связанного кода.

29
задан Jacek Laskowski 31 May 2015 в 12:46
поделиться

4 ответа

Ковариантность и контравариантность являются качествами класса , а не качествами параметров . (Это качества, которые зависят от параметров, но они делают утверждения о классе.)

Итак, Function1[-A,+B] означает, что можно просмотреть функцию , которая принимает суперклассы из A в качестве подкласса исходная функция .

Давайте посмотрим это на практике:

class A
class B extends A
val printB: B => Unit = { b => println("Blah blah") }
val printA: A => Unit = { a => println("Blah blah blah") }

Теперь предположим, что вам требуется функция, которая знает, как напечатать B:

def needsB(f: B => Unit, b: B) = f(b)

Вы можете передать в printB , Но вы также можете передать в printA, поскольку он также знает, как печатать B с (и больше!), Как если бы A => Unit был подклассом B => Unit. Это именно то, что означает контрвариантность. Это не значит, что вы можете передать Option[Double] в printB и получить что-либо, кроме ошибки времени компиляции!

(Covariance - другой случай: M[B] <: M[A] if B <: A.)

61
ответ дан Rex Kerr 31 May 2015 в 12:46
поделиться

Здесь работают две отдельные идеи. Одним из них является использование подтипов, позволяющих передавать более конкретные аргументы в функцию (называемую субсуммирование ). Другой - как проверить подтипы на самих функциях.

Для проверки типов аргументов функции вам нужно только проверить, что данные аргументы являются подтипами объявленных типов аргументов. Результат также должен быть подтипом объявленного типа. Здесь вы на самом деле проверяете подтипы.

Контраст / ко-дисперсия параметров & amp; результат учитывает только то, когда вы хотите проверить, является ли данный тип функции подтипом другого типа функции. Поэтому, если параметр имеет тип Function[A1, ... ,B], тогда аргумент должен быть типом функции Function[C1, ..., D], где A1 <: C1 ... и D <: B.

Это рассуждение не относится к Scala и применимо к другим статически типизированным языкам с подтипами.

6
ответ дан Asumu Takikawa 31 May 2015 в 12:46
поделиться

Этот вопрос старый, но я думаю, что более ясное объяснение состоит в том, чтобы вызвать принцип подстановки Лискова: все, что верно в отношении суперкласса, должно быть верным для всех его подклассов. Вы должны иметь возможность делать с SubFoo все, что вы можете делать с Foo, а может и больше.

Предположим, у нас есть Calico & lt ;: Cat & lt ;: Animal, а Husky & lt ;: Dog & lt ;: Animal. Давайте посмотрим на Function[Cat, Dog]. Какие утверждения правдивы по этому поводу? Их два:

(1) Вы можете передать любой Cat (так, любой подкласс Cat)

(2) Вы можете вызвать любой метод Dog для возвращаемого значения

Так что Function[Calico, Dog] <: Function[Cat, Dog] имеет смысл? Нет, операторы, которые являются истинными для суперкласса, не являются истинными для подкласса, а именно, оператор (1). Вы не можете передать ни одного кота в функцию, которая берет только кошек Калико.

Но имеет ли Function[Animal, Dog] <: Function[Cat, Dog] смысл? Да, все утверждения о суперклассе верны для подкласса. Я все еще могу передать любой Cat - фактически я могу сделать даже больше, я могу передать любой Animal - и я могу вызвать все методы Dog для возвращаемого значения.

Итак, A <: B подразумевает Function[B, _] <: Function[A, _]

Теперь, имеет ли Function[Cat, Husky] <: Function[Cat, Dog] смысл? Да, все утверждения о суперклассе верны для подкласса; Я все еще могу передать Cat, и я все еще могу вызывать все методы Dog для возвращаемого значения - на самом деле я могу сделать даже больше, я могу вызвать все методы Husky для возвращаемого значения.

Но имеет ли Function[Cat, Animal] <: Function[Cat, Dog] смысл? Нет, утверждения, которые являются истинными для суперкласса, не являются истинными для подкласса, а именно утверждения (2). Я не могу вызвать все методы, доступные в Dog для возвращаемого значения, только те, которые доступны в Animal.

Таким образом, с помощью Function[Animal, Husky] я могу делать все, что могу, с помощью Function[Cat, Dog]: я могу передать любой Cat, и я могу вызвать все методы Dog для возвращаемого значения. И я могу сделать еще больше: я могу перейти на других животных, и я могу вызвать методы, доступные на Husky, которые не доступны на Dog. Так что имеет смысл: Function[Animal, Husky] <: Function[Cat, Dog]. Первый параметр типа может быть заменен суперклассом, второй - подклассом.

26
ответ дан csjacobs24 31 May 2015 в 12:46
поделиться

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

Contra-вариант является противоположностью ковариант, что означает кошка к животному.

Инвариант означает, что он не может конвертировать.

-1
ответ дан kiritsuku 31 May 2015 в 12:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: