Глава 4. Организация кода в методах и процедурах. — КиберПедия 

История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...

Автоматическое растормаживание колес: Тормозные устройства колес предназначены для уменьше­ния длины пробега и улучшения маневрирования ВС при...

Глава 4. Организация кода в методах и процедурах.

2022-09-29 24
Глава 4. Организация кода в методах и процедурах. 0.00 из 5.00 0 оценок
Заказать работу

Хотя вы можете писать рабочий код строка за строкой, не структу-
рируя его, вы скорее всего не добьетесь многого до тех пор, пока не
будете способны повторно использовать код и хранить логику более мудрёными способами. Методы (иногда их называют функциями) позволяют группировать код в фрагменты, и вы даете им имена, чтоб иметь возможность вызывать их. В этой главе вы увидите подход Кристалла к методам.

Он схож с используемым в Ruby, хотя подход к типам данных в Крис-талле создает некоторые различия на пути к функциональному и чита-емому коду. Если вы чувствуете необходимость, просмотрите раздел "Использование методов", чтобы повторить введение в методы.

Поскольку в Crystal все является объектом, здесь есть методы "на все случаи жизни", даже для простых значений, таких как Int32 и Char. Операторы вроде / или <= также являются методами. Все, о чем мы поговорим в этой главе, также применяется и к тем методам, кото-
рые используются в классах (см. главу 5 §"Использование классов и структур").

К счастью, многое из того, что вы узнали о методах в других ЯП, применимо и здесь — мы рассмотрим основы в этой главе, но стоит отметить некоторые вещи, которые могут удивить вас, если вы перешли
из других языков программирования. Методы Crystal не живут внутри класса, в отличие от таких ЯП, как Java или C#. Вы можете также определить их (как мы уже много раз видели в предыдущих главах) в начале "исходников" вашей программы.

Это также распространяется на группирование связанных методов в модуле (см. §"Использование модулей в качестве пространств имен")
для последующего встраивания в классы.

Еще Crystal отличается широким кругозором. В отличие от ЯП Python или Ruby, Crystal не имеет глобальных переменных вообще. Перемен-ные, определяемые локально в методе, не будут видны вне метода. Обратное также верно в Crystal: переменные, которые вы определяете
вне метода, не будут видимы и внутри этого метода тоже.

methods_and_procs/methods.cr

x = 1

def add(y)

x + y # Error: undefined local variable or method  'x'

end

add(2)

 

Передача аргументов

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

methods_and_procs/methods.cr

def add(x, y)

# return x + y     # return is optional

x + y

end

 

1) add(2, 3) # => 5

2) add 2, 3 # => 5

3) add(1.0, 3.14) # => 4.14

4) add("Hello ", "Crystal") # => "Hello Crystal"

5) add(42, " times") # => Error: no overload matches 'Int32#+' with type String

6) add # => Error: wrong number of arguments for  'add'

#  (given 0, expected 2)

(Вы можете уточнить это в разделе §"Больше безопасности посредством Типов".)

 

При вызове метода можно опустить скобки (), как это сделано в
строке (2). Это здорово в тех случаях, когда у вас есть лишь несколько аргументов, но код быстро становится менее читаемым в более слож-
ных ситуациях. В нашей книге используется бесскобочная запись, где
это уместно.

Из строк 1, 3 и 4 вы видите, что мы можем с удовольствием
использовать утиную типизацию, как в любом динамическом языке. В утиной типизации вы позволяете ЯП автоматически определять тип объекта на основе его содержимого. Можно использовать метод add
для всех объектов "x" и "y" при условии, что их Type имеет сум-
мирующий метод, который является подходящим вариантом здесь для чисел и строк.

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

Еще Crystal неустанно сокращает вероятность программных сбоев, возникающих на стадии выполнения, посредством упреждающих
ошибок компилятора. А если вы (как в строке 6) передаете
недостаточно аргументов, возникает другая ошибка компилятора: «неправильное количество аргументов для add (задано 0 аргументов, ожидается 2)».

В разделе §"Больше безопасности посредством Типов" в сценариях overloading.cr,

 которые также используют метод add, мы выяснили, что Crystal позволяет типизировать аргументы. (Возможно, будет нелишним еще потренироваться с ними, прежде чем двигаться дальше.)

▪ ПОДВОХ ▪

В следующем фрагменте только аргумент "y" объявляется, как принадлежащий типу Int, но не "x":

 def add(x, y: Int)

x + y

 end

 

add 3, 4 # 7

add 2.14, 7 # 9.14

Во втором вызове метода add параметр "x" не соответствует типу Int. В случае, когда "x" и "y" оба должны соответствовать типу Int, опре-
делите метод так:

  add (x: Int, y: Int);

Crystal предлагает несколько более затейливых вариантов работы с аргументами. Вы можете указать для аргументов значения по умол-
чанию (default) в конце списка параметров — на тот случай, когда параметр для этого аргумента не задан, как в этом методе show:

methods_and_procs/methods.cr

def show(x, y = 1, z = 2, w = 3)

"x: #{x}, y: #{y}, z: #{z}, w: #{w}"

end

  

show 10 # => "x: 10, y: 1, z: 2, w: 3"

show 10, 10 # => "x: 10, y: 10, z: 2, w: 3"

show 10, 30, 2, 3 # => "x: 10, y: 30, z: 2, w: 3"

show 10, 20 # => "x: 10, y: 20, z: 2, w: 3"

 

show 10, z: 10 # => "x: 10, y: 1, z: 10, w: 3"

show 10, w: 30, y: 2, z: 3 # => "x: 10, y: 2, z: 3, w: 30"

show y: 10, x: 20 # => "x: 20, y: 10, z: 2, w: 3"

show y: 10 # Error, missing argument: x

 

Как можно увидеть во второй серии вызовов "show" в выше-
приведенном коде, вы можете указать аргументы по имени при вызове метода. Использование именованных аргументов подразумевает, что
мы не привязаны к их порядку.

Код также гораздо более читаем, если мы используем описательные имена, как в следующем примере, где мы создаем клиент авторизации, которому необходимы значения для аргументов "host", "client_id" и "client_secret":

require "oauth2"

client = OAuth2::Client.new(

host: "martian1",

client_id: "7594",

client_secret: "W*GDFUY75HSVS#@!"

 )

 

 

Возврат значений

 

Метод возвращает результирующее значение своего последнего вызова, поэтому нет необходимости напрямую запрашивать это значение или объявлять его тип. Однако, при необходимости документирования или непосредственного управления этим возвращаемым типом, вы можете особо указать тип, как в этом примере:

methods_and_procs/methods.cr

def typed_method: Array(Int32)

(42..47).to_a.select { |n| n % 4 == 0 }

end

 

typed_method # => [44]

 

Также можно явно использовать Nil в качестве возвращаемого типа,
если необходимо указать, что это "сопутствующий побочный метод" (важность имеет лишь то, что он делает, а не значение, которое он возвращает). Разработчикам ПО, использующим ваш метод, нужно
быть готовым к этому или, по крайней мере, это не должно быть сюрпризом.

Если необходимо вернуть несколько значений, их можно упаковать в кортеж или массив. Как распаковать значение с помощью деструкции (вернитесь к примеру Ваш черёд №4, "Извлечение данных из структуры вроде массива"):

methods_and_procs/methods.cr

# Multiple return values

def triple_and_array(s)

{s * 3, s.split}

end

  # unpacking:

ret = triple_and_array("42") # => {"424242", ["42"]}

ret[0] # => "424242"

ret[1] # => ["42"]

# or:

num, arr = triple_and_array("gold")

num # => "goldgoldgold"

arr # => ["gold"]

 

 

Использование аргумента SPLAT *

Если вы хотите, чтобы ваш метод принимал переменное количество параметров, то вы не можете дать имя каждому из ваших аргументов. Вместо этого используйте один аргумент с префиксом *, чтобы создать совместно вызываемый сплат-аргумент. И тогда метод может при-
нимать от нуля до неограниченного числа параметров, потому что
все они преобразованы в один кортеж.

К примеру, вам требуется рассчитать зарплаты для неизвестного числа сотрудников:

methods_and_procs/methods.cr

def salaries(*employees)

employees.each do |emp|

# calculate salary

puts "#{emp}'s salary is: 2500"

 end

end

salaries() # =>

salaries("Jones") # => Jones's salary is: 2500

salaries("Baudelaire", "Rogers", "Gandhi")

# =>

  # Baudelaire's salary is: 2500

  # Rogers's salary is: 2500

  # Gandhi's salary is: 2500

 

Использование сплат-аргумента не означает, что вы сами должны разбираться со всеми аргументами. Crystal предлагает другой способ, используя лишь одиночный *, чтобы указать, что все аргументы после символа * нужно снабдить именными метками. Такой подход рабо-
тает следующим образом:

def display(n, *, height, width)

"The shape has height #{height} and width #{width}"

end

 

display 3, height: 2, width: 5

  # => "The shape has height 2 and width 5"

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

methods_and_procs/methods.cr

def increment(number, by value)

number + value

end

p increment(10, by: 10) # => 20

Другой классической ситуацией является вульгарная проблема созда-
ния строки значений, разделенных неким символом, но так, чтобы этот символ не мог появиться после финального значения. Например, мы хотим "1-2-3 " но не "1-2-3- ".

Приведенное ниже решение принимает изменчивое количество значений args, размечая их разделителем вида "with joiner"; joiner — внутреннее имя, используемое лишь внутри метода, а "with" — это
обычное имя, используемое при вызове метода для передачи значения параметра:

methods_and_procs/methods.cr

def join(*args, with joiner)

String.build do |str|

args.each_with_index do |arg, index|

str << joiner if index > 0

str << arg

end

 end

end

join 1, 2, 3, with: "-" # => "1-2-3"

join 1, 2, 3, 4, 5, with: "*" # => "1*2*3*4*5"

Ваш черёд № 1

1. Итак: Напишите метод "total" для вычисления суммы произвольного количества чисел. Измените total так, чтобы суммирование начиналось с предопределенного значения.

2. Сплат-кортеж:

Вы также можете распаковать кортеж (который мы уже исследовали в упражнении Ваш черёд №4) прямиком в аргументы метода.

Предположим, что вы хотите сделать это:

def add(n, m)

n + m

end

tpl = {42, 108}

add tpl

Это работает? Можете ли вы объяснить это?

Вам следует назвать это так: добавление *tpl, которое иногда назы-
вается раскладыванием кортежа. Если tpl является именованным кортежом и вы хотите использовать именованные аргументы, вам придется применить двойной сплат **. Пробуйте этот прием.

Другой трюк состоит в использовании двойного сплат-аргумента — "**argument" — для захвата произвольного числа именованных параметров в именованный кортеж.

 


Поделиться с друзьями:

Индивидуальные очистные сооружения: К классу индивидуальных очистных сооружений относят сооружения, пропускная способность которых...

Типы сооружений для обработки осадков: Септиками называются сооружения, в которых одновременно происходят осветление сточной жидкости...

Состав сооружений: решетки и песколовки: Решетки – это первое устройство в схеме очистных сооружений. Они представляют...

Археология об основании Рима: Новые раскопки проясняют и такой острый дискуссионный вопрос, как дата самого возникновения Рима...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.061 с.