При обсуждении статических методов и методов экземпляров я всегда думаю, что это Sqrt()
должен быть метод экземпляров числовых типов, а не статический метод. Почему это? Это очевидно работает на значение.
// looks wrong to me
var y = Math.Sqrt(x);
// looks better to me
var y = x.Sqrt();
Типы значений, очевидно, могут иметь методы экземпляра, так как во многих языках есть метод экземпляра ToString()
.
Чтобы ответить на некоторые вопросы из комментариев: почему 1.Sqrt()
не должно быть законным? 1.ToString()
является.
Некоторые языки не позволяют иметь методы для типов значений, но некоторые языки могут. Я говорю об этом, в том числе Java, ECMAScript, C # и Python (с __str__(self)
определением). То же относится и к другим функциям, таким как ceil()
и floor()
т. Д.
1.sqrt()
действительным?Sqrt(x)
выглядит намного более естественным, чемx.Sqrt()
если бы это означало добавление функции к классу на некоторых языках, я согласен с этим. Если бы это был метод экземпляра, тоx.GetSqrt()
было бы более уместно указать, что он возвращает значение, а не модифицировать экземпляр.Ответы:
Это полностью выбор языка дизайна. Это также зависит от базовой реализации примитивных типов и соображений производительности, связанных с этим.
.NET имеет только один статический
Math.Sqrt
метод, который действует на adouble
и возвращает adouble
. Все, что вы передаете ему, должно быть разыграно или повышено доdouble
.С другой стороны, у вас есть Rust, который представляет эти операции как функции для типов :
Но у Rust также есть универсальный синтаксис вызова функций (кто-то упоминал об этом ранее), так что вы можете выбрать все, что захотите.
источник
x.Sqrt()
, это можно сделать.public static class DoubleExtensions { public static double Sqrt( this double self) { return Math.Sqrt(self); } }
Sqrt(x)
msdn.microsoft.com/en-us/library/sf0df423.aspx .Предположим, мы разрабатываем новый язык и хотим
Sqrt
быть методом экземпляра. Итак, мы смотрим наdouble
класс и начинаем проектировать. Очевидно, он не имеет входных данных (кроме экземпляра) и возвращает adouble
. Мы пишем и тестируем код. Совершенство.Но получение квадратного корня из целого числа также допустимо, и мы не хотим заставлять всех переходить в двойное число, чтобы получить квадратный корень. Итак, мы переходим
int
и начинаем проектирование. Что это возвращает? Мы могли бы вернутьint
и заставить его работать только для идеальных квадратов или округлить результат до ближайшегоint
(игнорируя споры о правильном методе округления на данный момент). Но что, если кто-то хочет нецелый результат? Если у нас есть два метода - один, который возвращаетint
и другой, который возвращаетdouble
(что невозможно в некоторых языках без изменения имени). Поэтому мы решили, что он должен вернутьdouble
. Сейчас мы реализуем. Но реализация идентична той, которую мы использовали дляdouble
, Мы копируем и вставляем? Мы приводим экземпляр кdouble
и вызываем этот метод экземпляра? Почему бы не поместить логику в метод библиотеки, к которому можно получить доступ из обоих классов. Мы назовем библиотекуMath
и функциюMath.Sqrt
.Мы даже не обратились к другим аргументам:
GetSqrt
так как оно возвращает новое значение, а не изменяет экземпляр?Square
?Abs
?Trunc
?Log10
?Ln
?Power
?Factorial
?Sin
?Cos
?ArcTan
?источник
1.sqrt()
vs1.1.sqrt()
(ghads, это выглядит ужасно), у них есть общий базовый класс? Какой контракт на егоsqrt()
метод?1.1.Sqrt
представляет. Умная.Математические операции часто очень чувствительны к производительности. Поэтому мы хотим использовать статические методы, которые могут быть полностью разрешены (и оптимизированы или встроены) во время компиляции. Некоторые языки не предлагают какого-либо механизма для определения статически отправляемых методов. Кроме того, объектная модель многих языков имеет значительные накладные расходы памяти, что недопустимо для «примитивных» типов, таких как
double
.Несколько языков позволяют нам определять функции, которые используют синтаксис вызова метода, но фактически отправляются статически. Методы расширения в C # 3.0 или позже являются примером. Не виртуальные методы (например, по умолчанию для методов в C ++) - другой случай, хотя C ++ не поддерживает методы для примитивных типов. Конечно, вы можете создать свой собственный класс-оболочку в C ++, который декорирует примитивный тип различными методами, без каких-либо накладных расходов во время выполнения. Однако вам придется вручную конвертировать значения в этот тип оболочки.
Есть несколько языков, которые определяют методы для своих числовых типов. Обычно это высокодинамичные языки, где все является объектом. Здесь производительность является второстепенным фактором для концептуальной элегантности, но эти языки обычно не используются для обработки чисел. Однако эти языки могут иметь оптимизатор, который может «распаковывать» операции с примитивами.
Принимая во внимание технические соображения, мы можем рассмотреть, будет ли такой математический интерфейс на основе методов хорошим интерфейсом. Возникают две проблемы:
42.sqrt
будет казаться гораздо более чуждым для многих пользователей, чемsqrt(42)
. Как математик, я бы предпочел возможность создавать свои собственные операторы по сравнению с синтаксисом вызова метода точек.mean
,median
,variance
,std
,normalize
числовые списки, или гамма - функции для чисел) может быть полезным. Для языка общего назначения это просто отягощает интерфейс. Отнесение несущественных операций в отдельное пространство имен делает тип более доступным для большинства пользователей.источник
transpose
иmean
предназначены только для обеспечения единого интерфейса с массивами NumPy, которые являются реальной рабочей лошадью.Меня мотивирует тот факт, что существует множество математических функций специального назначения, и вместо того, чтобы заполнять каждый математический тип всеми (или случайными подмножествами) тех функций, которые вы помещаете в служебный класс. В противном случае вы либо загрязните всплывающую подсказку об автозаполнении, либо заставите людей всегда искать в двух местах. (Это
sin
достаточно важно, чтобы быть членомDouble
, или это вMath
классе вместе с инбредами, какhtan
иexp1p
?)Другая практическая причина состоит в том, что оказывается, что могут быть разные способы реализации численных методов, с разной компромиссной производительностью и точностью. Ява есть
Math
и естьStrictMath
.источник
Math.<^space>
? Эта автозаполненная подсказка также будет загрязнена. И наоборот, я думаю, что ваш второй абзац, вероятно, один из лучших ответов здесь.Вы правильно заметили, что здесь присутствует любопытная симметрия.
Неважно, скажу я
sqrt(n)
илиn.sqrt()
нет, они оба выражают одно и то же, и то, что вы предпочитаете, является вопросом личного вкуса, а не всего остального.Вот почему у некоторых разработчиков языков есть веские аргументы в пользу взаимозаменяемости двух синтаксисов. Язык программирования D уже позволяет это с помощью функции, называемой Uniform Function Call Syntax . Аналогичная функция была также предложена для стандартизации в C ++ . Как отмечает Марк Эмери в комментариях , Python это тоже позволяет.
Это не без проблем. Внедрение такого фундаментального изменения синтаксиса имеет широкие последствия для существующего кода и, конечно, также является предметом спорных дискуссий среди разработчиков, которые десятилетиями обучались думать о двух синтаксисах как об описывающих разные вещи.
Я полагаю, только время покажет, возможно ли объединение двух вариантов в долгосрочной перспективе, но это, безусловно, интересное соображение.
источник
self
качестве своего первого параметра, и когда вы вызываете метод как свойство экземпляра, а не как свойство класса, экземпляр неявно передается в качестве первого аргумента. Следовательно, я могу написать"foo".startswith("f")
илиstr.startswith("foo", "f")
, и я могу написатьmy_list.append(x)
илиlist.append(my_list, x)
.В дополнение к ответу Д. Стенли вам нужно подумать о полиморфизме. Методы типа Math.Sqrt всегда должны возвращать одно и то же значение для одного и того же ввода. Создание статического метода - хороший способ прояснить этот момент, так как статические методы не могут быть переопределены.
Вы упомянули метод ToString (). Здесь вы можете переопределить этот метод, чтобы (под) класс был представлен другим способом как String как его родительский класс. Таким образом, вы делаете это методом экземпляра.
источник
Ну, в Java есть обертка для каждого базового типа.
А базовые типы не являются типами классов и не имеют функций-членов.
Итак, у вас есть следующие варианты:
Math
.Давайте исключим вариант 4, потому что ... Java - это Java, и приверженцы утверждают, что ему это нравится.
Теперь мы также можем исключить вариант 3, потому что, хотя выделение объектов довольно дешево, оно не бесплатное, и повторять это снова и снова не приходится.
Два ниже, один еще нужно убить: Вариант 2 также является плохой идеей, поскольку он означает, что каждая функция должна быть реализована для каждого типа, нельзя полагаться на расширение преобразования, чтобы заполнить пробелы, иначе несоответствия действительно повредят.
И, глядя на это
java.lang.Math
, есть много пробелов, особенно для типов меньшеint
соответствующихdouble
.Итак, в конце концов, чистый победитель - первый вариант, собирая их всех в одном месте в классе полезности-функции.
Возвращаясь к варианту 4, что-то в этом направлении на самом деле произошло намного позже: вы можете попросить компилятор учитывать все статические члены любого класса, который вы хотите, при разрешении имен в течение достаточно долгого времени.
import static someclass.*;
Кроме того, другие языки не имеют такой проблемы, потому что они не имеют никаких предубеждений против свободных функций (возможно, с использованием пространств имен) или гораздо меньше мелких типов.
источник
Math.min()
во всех типах оболочек.Math.sqrt()
был создан в то же время, что и остальная часть Java, поэтому, когда было принято решение включить sqrt (),Math
не было исторической инерции пользователей Java, которым «так нравится». Хотя проблем с этим не так многоsqrt()
, перегрузочное поведениеMath.round()
ужасно. Возможность использовать синтаксис члена со значениями типаfloat
иdouble
избежала бы этой проблемы.Одна вещь, которую я не вижу в явном виде (хотя и намекает на это), заключается в том, что квадратный корень можно рассматривать как «производную» операцию: если реализация не предоставляет ее нам, мы можем написать нашу собственную.
Поскольку вопрос помечен языковым дизайном, мы могли бы рассмотреть некоторые не зависящие от языка описания. Хотя многие языки придерживаются разных принципов, в парадигмах очень распространено использование инкапсуляции для сохранения инвариантов; т.е. чтобы избежать значения, которое не ведет себя так, как подсказывает его тип.
Например, если у нас есть некоторая реализация целых чисел, использующая машинные слова, мы, вероятно, хотим как-то инкапсулировать представление (например, чтобы битовые сдвиги не могли изменить знак), но в то же время нам все еще нужен доступ к этим битам для реализации таких операций, как дополнение.
Некоторые языки могут реализовать это с помощью классов и частных методов:
Некоторые с модульными системами:
Некоторые с лексической областью применения:
И так далее. Однако ни один из этих механизмов не нужен для реализации квадратного корня: он может быть реализован с использованием открытого интерфейса числового типа и, следовательно, ему не требуется доступ к инкапсулированным деталям реализации.
Следовательно, расположение квадратного корня сводится к философии / вкусам языка и дизайнера библиотеки. Некоторые из них могут выбрать , чтобы положить его «внутри» числовые значения (например , сделать это метод экземпляра), некоторые из них могут выбрать , чтобы положить его на том же уровне, что и примитивных операций (это может означать метод экземпляра, или это может означать , живущих за пределами числовые значения, но внутри одного и того же модуля / класса / пространства имен (например, в качестве автономной функции или статического метода), некоторые могут захотеть поместить его в набор «вспомогательных» функций, некоторые могут делегировать его сторонним библиотекам.
источник
В Java и C # ToString является методом объекта, корнем иерархии классов, поэтому каждый объект будет реализовывать метод ToString. Для типа Integer вполне естественно, что реализация ToString будет работать таким образом.
Таким образом, вы рассуждаете неправильно. Причина, по которой типы значений реализуют ToString, заключается не в том, что некоторые люди были похожи: эй, давайте иметь метод ToString для типов значений. Это потому, что ToString уже существует и это «самая естественная» вещь для вывода.
источник
object
иметьToString()
метод. Это в ваших словах "некоторые люди были похожи: эй, давайте иметь метод ToString для типов значений".В отличие от String.substring, Number.sqrt на самом деле является не атрибутом числа, а новым результатом, основанным на вашем номере. Я думаю, что передача вашего числа в функцию возведения в квадрат более интуитивно понятна.
Более того, объект Math содержит другие статические члены, и имеет больше смысла собирать их вместе и использовать их единообразно.
источник