Что такое скоуп в программировании
Перейти к содержимому

Что такое скоуп в программировании

  • автор:

Какое правильное определение у SCOPE (Скоуп)

Часто сталкиваюсь с таким понятием как scope но не понимаю его. Скажите точное определение что такое scope и прошу приложить 2-3 примера по возможности на php .

Отслеживать
задан 17 мар 2016 в 15:25
user199588 user199588
409 2 2 золотых знака 6 6 серебряных знаков 14 14 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Область видимости переменной — это контекст, в котором эта переменная определена. В большинстве случаев все переменные PHP имеют только одну область видимости. Эта единая область видимости охватывает также включаемые (include) и требуемые (require) файлы. Например:

Здесь переменная $a будет доступна внутри включенного скрипта b.inc . Однако определение (тело) пользовательской функции задает локальную область видимости данной функции. Любая используемая внутри функции переменная по умолчанию ограничена локальной областью видимости функции. Например:

 test(); ?> 

Этот скрипт не сгенерирует никакого вывода, поскольку выражение echo указывает на локальную версию переменной $a , а в пределах этой области видимости ей не было присвоено значение. Возможно вы заметили, что это немного отличается от языка C в том, что глобальные переменные в C автоматически доступны функциям, если только они не были перезаписаны локальным определением. Это может вызвать некоторые проблемы, поскольку люди могут нечаянно изменить глобальную переменную. В PHP, если глобальная переменная будет использоваться внутри функции, она должна быть объявлена глобальной внутри определения функции.

Скоупы — PHP: Eloquent (ORM)

Чем больше становится проект и чем сложнее его логика, тем чаще возникает потребность в построении запросов с условиями, группировками и сортировками. В коде проекта все чаще начнёт возникать подобный код:

 $posts = App\Models\Post::where('state', 'active') ->whereIn('user_id', $users) ->where('votes_count', '>', 100) ->orderBy('id', 'desc'); 

Иногда такие запросы могут встречаться прямо в обработчиках запросов. Но если начать ими злоупотреблять, то код постепенно начнёт превращаться в кашу.

Проблем здесь несколько. Не все условия могут быть очевидны. Например, что такое 100 в примере выше? Почему именно 100? Магическое число. Кроме того, если это число имеет какое-то особое значение, то вероятно оно будет встречаться и в других обработчиках точно в таком же запросе. Это значит что произойдёт дублирование.

Некоторые части запроса могут иметь значение только тогда, когда они встречаются вместе и их нужно указывать всегда. Например, выше мы выбираем только активные посты, у которых больше 100 просмотров. Можно предположить, что эти два условия связаны между собой, возможно в этом запросе подразумевается поиск «популярных постов». Это значит, что в любом месте где нам понадобятся популярные посты, нужно не забыть скопировать эти два условия.

Для решения этих проблем нужно повышать уровень абстракции – создавать функции, которые прячут в себе эти детали. Их можно сделать самостоятельно, но Eloquent уже имеет встроенный механизм называемый скоупами (Scope).

Его принцип работы крайне прост. Каждое условие или набор условий, которые мы бы хотели как-то обозначить функцией, можно задать в виде скоупа. Скоуп в терминах ORM и есть функция, которая определяется прямо в модели и используется фреймворком при построении запросов:

 namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model  public function scopeActive($query)  return $query->where('state', 'active'); > public function scopePopular($query)  return $query->where('votes_count', '>', 100); > > 

Каждый скоуп — это обычный метод, который начинается с префикса scope. Именно по префиксу Eloquent понимает, что это скоуп. Внутрь скоупа передаётся запрос, на который можно навешивать дополнительные условия. Результатом работы любого скоупа должен быть скоуп.

Далее они становятся доступны как и любые другие методы языка запросов:

 // И как статический метод и как обычный метод $users = App\Models\User::popular()->active()->orderBy('created_at')->get(); 

Скоупы можно вызывать из скоупов. Это помогает снизить уровень дублирования при построении более сложных запросов. Например, если мы считаем, что популярность оценивается только среди активных постов, то это легко поправить, изменив соответствующий скоуп:

 public function scopePopular($query)  return $query->active()->where('votes_count', '>', 100); > 

Динамические скоупы

Некоторые скоупы зависят от параметров, передающихся в процессе составления запроса. Для этого достаточно описать эти параметры внутри скоупа после параметра $query :

 namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model  public function scopeOfType($query, $type)  return $query->where('type', $type); > > 

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Что такое скоуп в программировании

Область видимости или scope определяет контекст переменной, в рамках которого ее можно использовать. В Python есть два типа контекста: глобальный и локальный.

Глобальный контекст

Глобальный контекст подразумевает, что переменная является глобальной, она определена вне любой из функций и доступна любой функции в программе. Например:

name = "Tom" def say_hi(): print("Hello", name) def say_bye(): print("Good bye", name) say_hi() say_bye()

Здесь переменная name является глобальной и имеет глобальную область видимости. И обе определенные здесь функции могут свободно ее использовать.

Локальный контекст

В отличие от глобальных переменных локальная переменная определяется внутри функции и доступна только из этой функции, то есть имеет локальную область видимости:

def say_hi(): name = "Sam" surname = "Johnson" print("Hello", name, surname) def say_bye(): name = "Tom" print("Good bye", name) say_hi() say_bye()

В данном случае в каждой из двух функций определяется локальная переменная name. И хотя эти переменные называются одинаково, но тем не менее это две разных переменных, каждая из которых доступна только в рамках своей функции. Также в функции say_hi() определена переменная surname, которая также является локальной, поэтому в функции say_bye() мы ее использовать не сможем.

Скрытие переменных

Есть еще один вариант определения переменной, когда локальная переменная скрывают глобальную с тем же именем:

name = "Tom" def say_hi(): name = "Bob" # скрываем значение глобальной переменной print("Hello", name) def say_bye(): print("Good bye", name) say_hi() # Hello Bob say_bye() # Good bye Tom

Здесь определена глобальная переменная name. Однако в функции say_hi определена локальная переменная с тем же именем name. И если функция say_bye использует глобальную переменную, то функция say_hi использует локальную переменную, которая скрывает глобальную.

Если же мы хотим изменить в локальной функции глобальную переменную, а не определить локальную, то необходимо использовать ключевое слово global :

name = "Tom" def say_hi(): global name name = "Bob" # изменяем значение глобальной переменной print("Hello", name) def say_bye(): print("Good bye", name) say_hi() # Hello Bob say_bye() # Good bye Bob

nonlocal

Выражение nonlocal прикрепляет идентификатор к переменной из ближайщего окружающего контекста (за исключением глобального контекста). Обычно nonlocal применяется во вложенных функциях, когда надо прикрепить идентификатор за переменной или параметром окружающей внешней функции. Рассмотрим ситуацию, где это выражение может пригодиться:

def outer(): # внешняя функция n = 5 def inner(): # вложенная функция print(n) inner() # 5 print(n) outer() # 5

Здесь вложенная локальная функция inner() выводит на консоль значение переменной n , которая определена во внешней функции outer(). Затем в функции outer() вызывается внутренняя функция inner().

При вызове функции outer() здесь мы ожидаемо увидим на консоли два раза число 5. Однако в данном случае вложенная функция inner() просто получает значение. Теперь возьмем другую ситуацию, когда вложенная функция присваивает значение переменной:

def outer(): # внешняя функция n = 5 def inner(): # вложенная функция n = 25 print(n) inner() # 25 print(n) outer() # 5 # 25 - inner # 5 - outer

При присвоении значения во вложенной функции: n = 25 будет создаваться новая переменная n, которая скроет переменную n из окружающей внешней функции outer. В итоге мы получим при выводе два разных числа. Чтобы во вложенной функции указать, что идентификатор во вложенной функции будет представлять переменную из окружающей функции, применяется выражение nonlocal :

def outer(): # внешняя функция n = 5 def inner(): # вложенная функция nonlocal n # указываем, что n - это переменная из окружающей функции n = 25 print(n) inner() # 25 print(n) outer() # 25

Что такое скоуп в программировании

Scope-функции (можно перевести как «функции контекста» или «функции области видимости») позволяют выполнить для некоторого объекта некоторый код в виде лямбда-выражение. При вызове подобной функции, создается временная область видимости. В этой области видимости можно обращаться к объекту без использования его имени.

В Kotlin есть пять подобных функций: let , run , with , apply и also . Эти функции похожи по своему действию и различаются прежде всего по параметрам и возвращаемым результатам

let

Лямбда-выражение в функции let в качестве параметра it получает объект, для которого вызывается функция. Возвращаемый результат функции let представляет результат данного лямбда-выражения.

inline fun T.let(block: (T) -> R): R

Распространенным сценарием, где применяется данная функция, представляет проверка на null:

fun main() < val sam = Person("Sam", "sam@gmail.com") sam.email?.let< println("Email: $it") >// Email: sam@gmail.com // аналог без функции let //if(sam.email!=null) println("Email:$") val tom = Person("Tom", null) tom.email?.let < println("Email: $it") >// функция let не будет выполняться > data class Person(val name: String, val email: String?)

Допустим, мы хотим вывести значение свойства Email объекта Person. Но это свойство может хранить значение null (например, если электронный адрес у пользователя не установлен). С помощью выражения sam.email?. проверяем значение свойства email на null . Если email не равно null, то для строки в свойстве email вызывается функция let , которая создает временную область видимости и передает в нее значение свойства email через параметр it . Если же свойство email равно null, то функция let не вызывается.

Если лямбда-выражение вызывает лишь одну функцию, в которую передается параметр it, то можно сократить вызов — указать после оператора :: название вызываемой функции:

fun main() < val sam = Person("Sam", "sam@gmail.com") sam.email?.let(::println) // sam@gmail.com val tom = Person("Tom", "tom@gmail.com") tom.email?.let(::printEmail) // Email: tom@gmail.com >fun printEmail(email: String) < println("Email: $email") >data class Person(val name: String, var email: String?)

with

Лямбда-выражение в функции with в качестве параметра this получает объект, для которого вызывается функция. Возвращаемый результат функции with представляет результат данного лямбда-выражения.

inline fun with(receiver: T, block: T.() -> R): R

Функция with принимает объект, для которого надо выполнить блок кода, в качестве параметра. Далее в самом блоке кода мы можем обращаться к общедоступным свойствам и методам объекта без его имени.

Обычно функция with() применяется, когда надо выполнить над объектом набор операций как единое целое:

fun main() < val tom = Person("Tom", null) val emailOfTom = with(tom) < if(email==null) email = "$@gmail.com" email > println(emailOfTom) // tom@gmail.com > data class Person(val name: String, var email: String?)

В данном случае функция with получает объект tom, поверяет его свойство email — если оно равно null, то устанавливает его на основе его имени. В качестве результата функции возвращается значение свойства email.

run

Лямбда-выражение в функции run в качестве параметра this получает объект, для которого вызывается функция. Возвращаемый результат функции run представляет результат данного лямбда-выражения.

inline fun T.run(block: T.() -> R): R

Функция run похожа на функцию with за тем исключением, что run реализована как функция расширения. Функция run также принимает объект, для которого надо выполнить блок кода, в качестве параметра. Далее в самом блоке кода мы можем обращаться к общедоступным свойствам и методам объекта без его имени.

fun main() < val tom = Person("Tom", null) val emailOfTom = tom.run < if(email==null) email = "$@gmail.com" email > println(emailOfTom) // tom@gmail.com > data class Person(val name: String, var email: String?)

В данном случае функция run выполняет действия, аналогичные примеру с функцией with .

Реализация run как функции расширения упрощает проверку на null:

fun main() < val tom = Person("Tom", null) val validationResult = tom.email?.run ?: "undefined" println(validationResult) // undefined > data class Person(val name: String, var email: String?)

Также есть другая разновидность функции run() , которая просто позволяет выполнить некоторое лямбда-выражение:

fun main() < val randomText = run< "hello world">println(randomText) // hello world run < println("run function")>// run function >

apply

Лямбда-выражение в функции apply в качестве параметра this получает объект, для которого вызывается функция. Возвращаемым результатом функции фактически является передаваемый в функцию объект для которого выполняется функция.

inline fun T.apply(block: T.() -> Unit): T
fun main() < val tom = Person("Tom", null) tom.apply < if(email==null) email = "$@gmail.com" > println(tom.email) // tom@gmail.com > data class Person(val name: String, var email: String?)

В данном случае, как и в примерах с функциями with и run , проверяем значение свойства email, и если оно равно null, устанавливаем его, используя свойство name.

Распространенным сценарием применения функции apply() является построение объекта в виде реализации вариации паттерна «Строитель»:

fun main() < val bob = Employee() bob.name("Bob") bob.age(26) bob.company("JetBrains") println("$($) - $") // Bob (26) - JetBrains > data class Employee(var name: String = "", var age: Int = 0, var company: String = "") < fun age(_age: Int): Employee = apply < age = _age >fun name(_name: String): Employee = apply < name = _name >fun company(_company: String): Employee = apply < company = _company >>

В данном случае класс Employee содержит три метода, которые устанавливают каждое из свойств класса. Причем каждый подобный метод вызывает функцию apply() , которое передает значение соответствующему свойству и возвращает текущий объект Employee.

also

Лямбда-выражение в функции also в качестве параметра it получает объект, для которого вызывается функция. Возвращаемым результатом функции фактически является передаваемый в функцию объект для которого выполняется функция.

inline fun T.also(block: (T) -> Unit): T

Эта функция аналогична функции apply за тем исключением, что внутри also объект, над которым выполняется блок кода, доступен через параметр it :

fun main() < val tom = Person("Tom", null) tom.also < if(it.email==null) it.email = "$@gmail.com" > println(tom.email) // tom@gmail.com > data class Person(val name: String, var email: String?)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *