Модуль:Random

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
Документация

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

Number

Функция number возвращает случайное число.

{{#invoke:random|number|m|n|same=yes}}

Аргументы m и n могут быть опущены, однако в случае, если они заданы, они должны быть приводимы к целым числам.

  • При вызове без параметров возвращает вещественное число в полуинтервале .
  • При вызове с одним параметром возвращает целое число из интервала . m должно быть положительным.
  • При вызове с двумя параметрами возвращает целое число из интервала . m и n могут быть как положительными, так и отрицательными. Если m больше n, возвращает целое число из интервала .
  • Если параметру |same= присвоено одно из значений «yes», «y», «true» или «1», одно и то же случайное число возвращается для каждого вызова модуля на данной странице.

Примеры (обновить)

  • {{#invoke:random|number}} → 0.86885006952512
  • {{#invoke:random|number|100}} → 62
  • {{#invoke:random|number|-100|-50}} → -63
  • {{#invoke:random|number|100|same=yes}} → 12
  • {{#invoke:random|number|100|same=yes}} → 12

Документация этой функции частично скопирована из справки по Scribunto Lua, в свою очередь основанной на справке по Lua 5.1, доступной под лицензией MIT.

Date

Функция date возвращает случайную дату.

{{#invoke:random|date|метка1|метка2|format=формат даты|same=yes}}
  • Если ни одна из временных меток не задана, модуль возвращает случайную дату текущего года.
  • Если заданы параметры метка1 и метка2, модуль возвращает случайную дату между данными двумя временными метками. метка1 должна быть раньше метка2.
  • Если задан только параметр метка1, модуль возвращает случайную дату между «эрой Unix» (1 января 1970) и временной меткой. Метка метка1 не должна быть раньше 1 января 1970.
  • Форматирование может быть задано параметром |format=. Форматирование по умолчанию — «hh:mm, DD Month YYYY (UTC)» (аналогично форматированию по умолчанию временных меток Википедии).
  • Временные метки и параметр |format= поддерживают значения, совместимые с функцией парсера #time. Обратитесь к документации #time для получения полного списка возможных входных значений и опций форматирования.
  • Если параметру |same= присвоено одно из значений «yes», «y», «true» или «1», одна и та же дата возвращается для каждого вызова модуля на данной странице.

Примеры (обновить)

  • {{#invoke:random|date}} → 18:20, 29 март 2019 (UTC)
  • {{#invoke:random|date|format=F j}} → февраль 27
  • {{#invoke:random|date|1 Jan 1980|31 Dec 1999}} → 11:22, 17 март 1998 (UTC)
  • {{#invoke:random|date|1st January 1500|1st January 3000|format=g:i a, l d M Y}} → 4:03 am, среда 02 апр 2358
  • {{#invoke:random|date|1970/06/01}} → 16:40, 13 январь 1970 (UTC)
  • {{#invoke:random|date|same=yes}} → 08:34, 11 февраль 2019 (UTC)
  • {{#invoke:random|date|same=yes}} → 08:34, 11 февраль 2019 (UTC)

Item

Функция item возвращает случайный элемент списка.

{{#invoke:random|item|элемент списка 1|элемент списка 2|элемент списка 3|...|same=yes}}

Если параметру |same= присвоено одно из значений «yes», «y», «true» или «1», один и тот же элемент возвращается для каждого вызова модуля на данной странице.

Пример (обновить)

  • {{#invoke:random|item|egg|beans|sausage|bacon|spam}} → bacon
  • {{#invoke:random|item|egg|beans|sausage|bacon|spam|same=yes}} → beans
  • {{#invoke:random|item|egg|beans|sausage|bacon|spam|same=yes}} → beans

List

Функция list возвращает список в случайном порядке.

{{#invoke:random|list|элемент списка 1|элемент списка 2|элемент списка 3|...|sep=разделитель|limit=сколько элементов отображать|same=yes}}

Именованные параметры

  • |sep= или |separator= — опциональный разделитель списка элементов. Некоторые значения зарезервированы, см. таблицу ниже.
  • |limit= — максимальное число отображаемых элементов списка. Наименьшее возможное значение — 0, наибольшее — число элементов в списке.
  • Если параметру |same= присвоено одно из значений «yes», «y», «true» или «1», один и тот же список возвращается для каждого вызова модуля на данной странице.
Возможные значения разделителя
Код Вывод
dot  ·
pipe |
comma ,
tpt-languages  •
space пробел
newline символ перевода строки
любое другое значение остальные значения используются без изменения

Передать пробелы в параметр |sep= невозможно из-за ограничений, накладываемых синтаксисом шаблонов в MediaWiki. Однако их можно обойти, используя мнемоники HTML. Используйте   для представления обычного пробела и   для представления неразрывного пробела.

Примеры (обновить)

  • {{#invoke:random|list|egg|beans|sausage|bacon|spam}} → spambaconeggbeanssausage
  • {{#invoke:random|list|egg|beans|sausage|bacon|spam|sep=dot}} → egg · spam · sausage · bacon · beans
  • {{#invoke:random|list|egg|beans|sausage|bacon|spam|sep=space}} → bacon beans sausage egg spam
  • {{#invoke:random|list|egg|beans|sausage|bacon|spam|sep=; }} → spam; sausage; bacon; beans; egg
  • {{#invoke:random|list|egg|beans|sausage|bacon|spam|sep=foo}} → baconfoospamfoobeansfooeggfoosausage
  • {{#invoke:random|list|egg|beans|sausage|bacon|spam|limit=3}} → sausagespamegg
  • {{#invoke:random|list|egg|beans|sausage|bacon|spam|same=yes}} → spamsausagebaconeggbeans
  • {{#invoke:random|list|egg|beans|sausage|bacon|spam|same=yes}} → spamsausagebaconeggbeans

Text list

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

{{#invoke:random|text_list|элемент списка 1|элемент списка 2|элемент списка 3|...|sep=разделитель|conj=союз|limit=сколько элементов отображать|same=yes}}

Разделитель может быть задан параметрами |sep= или |separator=; значение по умолчанию — «, ». Союз может быть задан параметрами |conj= или |conjunction=; значение по умолчанию — « и ». В параметры разделителя и союза могут быть переданы те же значения, что и в параметр разделителя в функции list.

Максимальное число отображаемых элементов списка может быть задано параметром |limit=. Наименьшее возможное значение — 0, наибольшее — число элементов в списке.

Если параметру |same= присвоено одно из значений «yes», «y», «true» или «1», один и тот же список возвращается для каждого вызова модуля на данной странице.

Примеры (обновить)

  • {{#invoke:random|text_list|egg|beans|sausage|bacon|spam}} → egg, spam, sausage, beans и bacon
  • {{#invoke:random|text_list|egg|beans|sausage|bacon|spam|sep=; }} → egg; beans; sausage; bacon и spam
  • {{#invoke:random|text_list|egg|beans|sausage|bacon|spam|sep=; |conj= или }} → spam; sausage; bacon; egg или beans
  • {{#invoke:random|text_list|egg|beans|sausage|bacon|spam|limit=3}} → spam, sausage и egg
  • {{#invoke:random|text_list|egg|beans|sausage|bacon|spam|same=yes}} → spam, sausage, bacon, egg и beans
  • {{#invoke:random|text_list|egg|beans|sausage|bacon|spam|same=yes}} → spam, sausage, bacon, egg и beans

HTML-списки

Если вы хотите вывести HTML-список в случайном порядке, вы можете выбрать одну из пяти функций: bulleted_list, unbulleted_list, horizontal_list, ordered_list, и horizontal_ordered_list. Все эти функции используют Модуль:List.

Название функции Создаёт Код примера Вывод примера (обновить)
bulleted_list Маркированный список {{#invoke:random|bulleted_list|egg|sausage|spam}}
  • spam
  • sausage
  • egg
unbulleted_list Немаркированный список {{#invoke:random|unbulleted_list|egg|sausage|spam}}
  • spam
  • egg
  • sausage
horizontal_list Горизонтальный маркированный список {{#invoke:random|horizontal_list|egg|sausage|spam}}
  • egg
  • spam
  • sausage
ordered_list Упорядоченный список (нумерованные и алфавитные списки) {{#invoke:random|ordered_list|egg|sausage|spam}}
  1. egg
  2. spam
  3. sausage
horizontal_ordered_list Горизонтальный упорядоченный список {{#invoke:random|horizontal_ordered_list|egg|sausage|spam}}
  1. sausage
  2. spam
  3. egg
Базовое использование
{{#invoke:random|функция|элемент списка 1|элемент списка 2|элемент списка 3|...|limit=сколько элементов отображать|same=yes}}
Все параметры
{{#invoke:random|функция
|первый элемент|второй элемент|третий элемент|...
|start           = начальное число для упорядоченного списка
|type            = тип маркера для упорядоченного списка
|list_style_type = тип маркера для упорядоченного списка (использует CSS)
|class           = класс
|style           = стиль
|list_style      = стиль списка
|item_style      = стиль всех элементов списка
|item_style1     = стиль первого элемента списка |item_style2 = стиль второго элемента списка |...
|indent          = отступ для горизонтальных списков
}}

Максимальное число отображаемых элементов списка может быть задано параметром |limit=. Наименьшее возможное значение — 0, наибольшее — число элементов в списке.

Если параметру |same= присвоено одно из значений «yes», «y», «true» или «1», один и тот же список возвращается для каждого вызова модуля на данной странице.

Посетите документацию Модуля:List для получения полной информации по остальным параметрам.

См. также

-- This module contains a number of functions that make use of random numbers.

local cfg = {}

--------------------------------------------------------------------------------------
-- Configuration
--------------------------------------------------------------------------------------

-- Set this to true if your wiki has a traffic rate of less than one edit every two minutes or so.
-- This will prevent the same "random" number being generated many times in a row until a new edit is made
-- to the wiki. This setting is only relevant if the |same= parameter is set.
cfg.lowTraffic = false

-- If cfg.lowTraffic is set to true, and the |same= parameter is set, this value is used for the refresh rate of the random seed.
-- This is the number of seconds until the seed is changed. Getting this right is tricky. If you set it too high, the same number
-- will be returned many times in a row. If you set it too low, you may get different random numbers appearing on the same page,
-- particularly for pages that take many seconds to process.
cfg.seedRefreshRate = 60

--------------------------------------------------------------------------------------
-- End configuration
--------------------------------------------------------------------------------------

local p = {} -- For functions available from other Lua modules.
local l = {} -- For functions not available from other Lua modules, but that need to be accessed using table keys.

local yesno = require('Module:Yesno')
local makeList = require('Module:List').makeList

--------------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------------

local function raiseError(msg)
	-- This helps to generate a wikitext error. It is the calling function's responsibility as to how to include it in the output.
	return mw.ustring.format('<b class="error">[[Module:Random]] error: %s.</b>', msg)
end

--------------------------------------------------------------------------------------
-- random number function
--------------------------------------------------------------------------------------

local function getBigRandom(l, u)
	-- Gets a random integer between l and u, and is not limited to RAND_MAX.
	local r = 0
	local n = 2^math.random(30) -- Any power of 2.
	local limit = math.ceil(53 / (math.log(n) / math.log(2)))
	for i = 1, limit do
		r = r + math.random(0, n - 1) / (n^i)
	end
	return math.floor(r * (u - l + 1)) + l
end

function l.number(args)
	-- Gets a random number.
	first = tonumber(args[1])
	second = tonumber(args[2])
	-- This needs to use if statements as math.random won't accept explicit nil values as arguments.
	if first then
		if second then
			if first > second then -- Second number cannot be less than the first, or it causes an error.
				first, second = second, first
			end
			return getBigRandom(first, second)
		else
			return getBigRandom(1, first)
		end
	else
		return math.random()
	end
end

--------------------------------------------------------------------------------------
-- Date function
--------------------------------------------------------------------------------------

function l.date(args)
	-- This function gets random dates, and takes timestamps as positional arguments.
	-- With no arguments specified, it outputs a random date in the current year.
	-- With two arguments specified, it outputs a random date between the timestamps.
	-- With one argument specified, the date is a random date between the unix epoch (1 Jan 1970) and the timestamp.
	-- The output can be formatted using the "format" argument, which works in the same way as the #time parser function.
	-- The default format is the standard Wikipedia timestamp.
	local lang = mw.language.getContentLanguage()

	local function getDate(format, ts)
		local success, date = pcall(lang.formatDate, lang, format, ts)
		if success then
			return date
		end
	end

	local function getUnixTimestamp(ts)
		local unixts = getDate('U', ts)
		if unixts then
			return tonumber(unixts)
		end
	end

	local t1 = args[1]
	local t2 = args[2]
	
	-- Find the start timestamp and the end timestamp.
	local startTimestamp, endTimestamp
	if not t1 then
		-- Find the first and last second in the current year.
		local currentYear = tonumber(getDate('Y'))
		local currentYearStartUnix = tonumber(getUnixTimestamp('1 Jan ' .. tostring(currentYear)))
		local currentYearEndUnix = tonumber(getUnixTimestamp('1 Jan ' .. tostring(currentYear + 1))) - 1
		startTimestamp = '@' .. tostring(currentYearStartUnix) -- @ is used to denote Unix timestamps with lang:formatDate.
		endTimestamp = '@' .. tostring(currentYearEndUnix)
	elseif t1 and not t2 then
		startTimestamp = '@0' -- the Unix epoch, 1 January 1970
		endTimestamp = t1
	elseif t1 and t2 then
		startTimestamp = t1
		endTimestamp = t2
	end

	-- Get Unix timestamps and return errors for bad input (or for bugs in the underlying PHP library, of which there are unfortunately a few)
	local startTimestampUnix = getUnixTimestamp(startTimestamp)
	local endTimestampUnix = getUnixTimestamp(endTimestamp)
	if not startTimestampUnix then
		return raiseError('"' .. tostring(startTimestamp) .. '" was not recognised as a valid timestamp')
	elseif not endTimestampUnix then
		return raiseError('"' .. tostring(endTimestamp) .. '" was not recognised as a valid timestamp')
	elseif startTimestampUnix > endTimestampUnix then
		return raiseError('the start date must not be later than the end date (start date: "' .. startTimestamp .. '", end date: "' .. endTimestamp .. '")')
	end

	-- Get a random number between the two Unix timestamps and return it using the specified format.
	local randomTimestamp = getBigRandom(startTimestampUnix, endTimestampUnix)
	local dateFormat = args.format or 'H:i, d F Y (T)'
	local result = getDate(dateFormat, '@' .. tostring(randomTimestamp))
	if result then
		return result
	else
		return raiseError('"' .. dateFormat .. '" is not a valid date format')
	end
end

--------------------------------------------------------------------------------------
-- List functions
--------------------------------------------------------------------------------------

local function randomizeArray(t, limit)
	-- Randomizes an array. It works by iterating through the list backwards, each time swapping the entry
	-- "i" with a random entry. Courtesy of Xinhuan at http://forums.wowace.com/showthread.php?p=279756
	-- If the limit parameter is set, the array is shortened to that many elements after being randomized.
	-- The lowest possible value is 0, and the highest possible is the length of the array.
	local len = #t
	for i = len, 2, -1 do
		local r = math.random(i)
		t[i], t[r] = t[r], t[i]
	end
	if limit and limit < len then
		local ret = {}
		for i, v in ipairs(t) do
			if i > limit then
				break
			end
			ret[i] = v
		end
		return ret
	else
		return t
	end
end

local function removeBlanks(t)
	-- Removes blank entries from an array so that it can be used with ipairs.
	local ret = {}
	for k, v in pairs(t) do
		if type(k) == 'number' then
			table.insert(ret, k)
		end
	end
	table.sort(ret)
	for i, v in ipairs(ret) do
		ret[i] = t[v]
	end
	return ret
end

local function makeSeparator(sep)
	if sep == 'space' then
		-- Include an easy way to use spaces as separators.
		return ' '
	elseif sep == 'newline' then
		-- Ditto for newlines
		return '\n'
	elseif type(sep) == 'string' then
		-- If the separator is a recognised MediaWiki separator, use that. Otherwise use the value of sep if it is a string.
		local mwseparators = {'dot', 'pipe', 'comma', 'tpt-languages'}
		for _, mwsep in ipairs(mwseparators) do
			if sep == mwsep then
				return mw.message.new( sep .. '-separator' ):plain()
			end
		end
		return sep
	end
end

local function makeRandomList(args)
	local list = removeBlanks(args)
	list = randomizeArray(list, tonumber(args.limit))
	return list
end

function l.item(args)
	-- Returns a random item from a numbered list.
	local list = removeBlanks(args)
	local len = #list
	if len >= 1 then
		return list[math.random(len)]
	end
end

function l.list(args)
	-- Randomizes a list and concatenates the result with a separator.
	local list = makeRandomList(args)
	local sep = makeSeparator(args.sep or args.separator)
	return table.concat(list, sep)
end

function l.text_list(args)
	-- Randomizes a list and concatenates the result, text-style. Accepts separator and conjunction arguments.
	local list = makeRandomList(args)
	local sep = makeSeparator(args.sep or args.separator)
	local conj = makeSeparator(args.conj or args.conjunction)
	return mw.text.listToText(list, sep, conj)
end

function l.array(args)
	-- Returns a Lua array, randomized. For use from other Lua modules.
	return randomizeArray(args.t, args.limit)
end

--------------------------------------------------------------------------------------
-- HTML list function
--------------------------------------------------------------------------------------

function l.html_list(args, listType)
	-- Randomizes a list and turns it into an HTML list. Uses [[Module:List]].
	listType = listType or 'bulleted'
	local listArgs = makeRandomList(args) -- Arguments for [[Module:List]].
	for k, v in pairs(args) do
		if type(k) == 'string' then
			listArgs[k] = v
		end
	end
	return makeList(listType, listArgs)
end

--------------------------------------------------------------------------------------
-- The main function. Called from other Lua modules.
--------------------------------------------------------------------------------------

function p.main(funcName, args, listType)
	-- Sets the seed for the random number generator and passes control over to the other functions.
	local same = yesno(args.same)
	if not same then
		-- Generates a different number every time the module is called, even from the same page.
		-- This is because of the variability of os.clock (the time in seconds that the Lua script has been running for).
		math.randomseed(mw.site.stats.edits + mw.site.stats.pages + os.time() + math.floor(os.clock() * 1000000000))
	else
		if not cfg.lowTraffic then
			-- Make the seed as random as possible without using anything time-based. This means that the same random number
			-- will be generated for the same input from the same page - necessary behaviour for some wikicode templates that
			-- assume bad pseudo-random-number generation.
			local stats = mw.site.stats
			local views = stats.views or 0 -- This is not always available, so we need a backup.
			local seed = views + stats.pages + stats.articles + stats.files + stats.edits + stats.users + stats.activeUsers + stats.admins -- Make this as random as possible without using os.time() or os.clock()
			math.randomseed(seed)
		else
			-- Make the random seed change every n seconds, where n is set by cfg.seedRefreshRate.
			-- This is useful for low-traffic wikis where new edits may not happen very often.
			math.randomseed(math.floor(os.time() / cfg.seedRefreshRate))
		end
	end
	if type(args) ~= 'table' then
		error('the second argument to p.main must be a table')
	end
	return l[funcName](args, listType)
end
	
--------------------------------------------------------------------------------------
-- Process arguments from #invoke
--------------------------------------------------------------------------------------

local function makeWrapper(funcName, listType)
	-- This function provides a wrapper for argument-processing from #invoke.
	-- listType is only used with p.html_list, and is nil the rest of the time.
	return function (frame)
		-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
		-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
		local origArgs
		if frame == mw.getCurrentFrame() then
			origArgs = frame:getParent().args
			for k, v in pairs(frame.args) do
				origArgs = frame.args
				break
			end
		else
			origArgs = frame
		end
		-- Trim whitespace and remove blank arguments.
		local args = {}
		for k, v in pairs(origArgs) do
			if type(v) == 'string' then
				v = mw.text.trim(v)
			end
			if v ~= '' then
				args[k] = v
			end
		end
		return p.main(funcName, args, listType)
	end
end

-- Process arguments for HTML list functions.
local htmlListFuncs = {
	bulleted_list           = 'bulleted',
	unbulleted_list         = 'unbulleted',
	horizontal_list         = 'horizontal',
	ordered_list            = 'ordered',
	horizontal_ordered_list = 'horizontal_ordered'
}
for funcName, listType in pairs(htmlListFuncs) do
	p[funcName] = makeWrapper('html_list', listType)
end

-- Process arguments for other functions.
local otherFuncs = {'number', 'date', 'item', 'list', 'text_list'}
for _, funcName in ipairs(otherFuncs) do
	p[funcName] = makeWrapper(funcName)
end

return p