5

EDIT Since 8.1.2342, Vim introduced a rand() function as described by @Maxim Kim's answer. I'll keep the question here for historical reference.


I recently needed to generate a random number in vim and this question gave me all the solutions I needed.

But I still have a question: Why is there no built-in function to generate a random number?

I mean the vimscript language provides functions to work with numbers like round(), min(), the basic operators +-*/, so what explains that the absence of a rand() function?

For now I see 2 possible explanations but I wasn't able to validate any of them:

  • Technical limitations: I know the bad reputation of this language but I don't see what would make it unable to generate a random number.

  • "Ideological" limitation: The creator of vim script could have decided that the language being made to edit text, doesn't need to generate random numbers and this functionality shouldn't be available to the user. I'm not a Vim guru but I don't understand how including such a function would hurt anything or anyone.

Is there another reason that I couldn't figure out by myself? Could someone enlighten me on this?

4
  • I can't see that such a function would make sense in an editor - but that, of course, doesn't mean there couldn't be a sensible application; and I'm certainly keen to know if there's any and why you are specifically asking for one. - Just one point: "a rand() function won't hurt" is just about the worst argument to introduce one.
    – Janis
    Jul 5 '15 at 17:56
  • @Janis: I agree that it is not necessarily a core feature of an editor. I'm using it to shuffle the letters of a string (really that's just to learn vimscript I will not say that this plugin will change the world). So I'm not specifically asking for it I was simply genuinely wondering why it isn't implemented. Your last point is totally fair that's not how a feature should be added in a software I can only agree with you :-)
    – statox
    Jul 5 '15 at 19:48
  • 1
    @Janis Some places where it would be useful in an editor: creating UUID's, creating uniquely named temp files/dirs (e.g. mkstemp()), creating "secrets" (many applications have a "secret" for encrypting cookies and whatnot)... Jul 7 '15 at 9:04
  • @Carpetsmoker; it's obvious that there's a lot places where random numbers are helpful. But you didn't explain why that would be a sensible feature in an editor. In my book, an editor is used to create and modify texts and not to perform shell tasks like creating temporary files. To obtain functions of external commands vi and vim also support a standard interface, so it seems unnecessary to implement every possible function in the editor. But, again; feel free to elaborate on the necessity for a built-in.
    – Janis
    Jul 8 '15 at 3:00
9

Because the patches have been sitting in the TODO list for almost five years.

Don't forget, vimscript wasn't designed: it is only the ever-changing interim result of a slow evolutionary process. Hell, it didn't even have an actual name in the doc until "VimL" was discreetly slipped in a bit before 7.4.

6
  • Err, five years is a rather conservative estimate. :) Meanwhile, @christian-brabandt has gathered some of these patches in a repository. There is a rand_funtion among them. Jul 5 '15 at 19:55
  • Wow 5 years in a TODO list... let's hope that a mature version of Neovim will solve this kind of things. Also does it mean that the maintainers would be ok to include the patch in vim but don't have the time to do so or that they just don't want the patch to be included (I'm not really aware of the way new features are included into vim). And finally interesting point, I didn't know the history of the vimscript (or VimL) name!
    – statox
    Jul 5 '15 at 19:56
  • @statox Neovim did solve the problem of patches sitting in a queue for years. Its the "getting mature" part that some people are skeptic about. Jul 5 '15 at 19:59
  • @SatoKatsura: About the patch I had understood that they wanted to make the code more maintainable and easier to patch by a greater number of people, that's why I mentioned that. On the "getting mature" part I have to say that I haven't studied the project enough to have a real opinion about it.
    – statox
    Jul 5 '15 at 20:04
  • 1
    To be fair, bug fixes are merged a lot faster, usually within a few days. The vast majority of patches accepted for 7.3 and 7.4 (~2000 patches) have been bug fixes.
    – lcd047
    Jul 6 '15 at 3:39
7

Well, generating random numbers isn't really the task of a an editor, IMHO. And with Vim, "the Unix way" to do it is to rely on other tools, that do it ... better. E.g. you can:

  • on *nix systems, read "/dev/urandom" or similar
  • call a program that does it
  • write a plugin in python, ruby, lua etc. - all languages with 'random' support.

Simple, non-security concerned random functionality can be provided directly with vimscript, though, by reading ... time microseconds values. Here's how it is implemented with the vim-randomtag plugin:

function! s:randnum(max) abort
  return str2nr(matchstr(reltimestr(reltime()), '\v\.@<=\d+')[1:]) % a:max
endfunction

P.S. I'm not saying that it won't be useful to have a function in vimscript, though ...

2
  • Indeed the seems to follow the unix philosophy. By the way, the function you give in your answer works pretty good and it isn't listed in the answers of the question I'm referring to in my question, maybe you should add an answer that could be useful to other users :-)
    – statox
    Jul 5 '15 at 20:01
  • ok, i added the answer.
    – VanLaser
    Jul 5 '15 at 22:37
4

Vim has rand() function since 8.1.2342

rand([{expr}])                      *rand()* *random*
        Return a pseudo-random Number generated with an xoshiro128**
        algorithm using seed {expr}.  The returned number is 32 bits,
        also on 64 bits systems, for consistency.
        {expr} can be initialized by |srand()| and will be updated by
        rand().  If {expr} is omitted, an internal seed value is used
        and updated.

        Examples: >
            :echo rand()
            :let seed = srand()
            :echo rand(seed)
            :echo rand(seed) % 16  " random number 0 - 15
2
  • 1
    +1. See here for a short list of usage examples.
    – user938271
    Oct 19 '20 at 8:53
  • Ah cool, I hadn't seen that it was added thanks :) I'll edit my question
    – statox
    Oct 19 '20 at 12:08
0

If you happen to use neovim, you can also use luaeval() to generate random numbers (this is for neovim only, does not work in Vim).

" math.randomseed() is need to make the random() function generate different numbers 
" on each use. Otherwise, the first number it generate seems same all the time.
luaeval('math.randomseed(os.time())')
let num = luaeval('math.random(1, 10)')

Based on my benchmark, the luaeval() code is about twice as fast as the code based on vim script.

0

As I try to provide portable plugins I have the following in my library plugin. I should be able to detect and use rand() now.

" Function: lh#common#rand(max) {{{2
" This function requires ruby, and it may move to another autoload plugin
if has('ruby')
  function! lh#common#rand_ruby(max)
    ruby << EOF
    rmax = VIM::evaluate("a:max")
    rmax = nil if rmax == ""
    VIM::command("return #{rand(rmax).inspect}")
EOF
  endfunction
  let s:random = get(s:, 'random', 'ruby')
endif

if !exists('s:random')
  " TODO: use pyx!
  let py_flavour = lh#python#best_still_avail()
  if !empty(py_flavour)
    function! lh#common#rand_python3(max)
python3 << EOF
import vim, random
rmax = eval(vim.eval("a:max")) - 1
# rmax = nil if rmax == ""
res = random.randint(0, rmax)
vim.command("return %d" % (res,))
EOF
    endfunction
    function! lh#common#rand_python(max)
python << EOF
import vim, random
rmax = eval(vim.eval("a:max")) - 1
# rmax = nil if rmax == ""
res = random.randint(0, rmax)
vim.command("return %d" % (res,))
EOF
    endfunction
    let s:random = get(s:, 'random', py_flavour)
  endif
endif

if lh#os#system_detected() == 'unix'
  " This flavour is very slow, and best avoided!
  function! lh#common#rand_unix(max)
    let nb_bytes = float2nr(log(a:max-1)/s:k_lg256)+1
    let rnd = matchstr(system('echo $RANDOM'), '\d\+')
    let res = rnd % a:max
    return res
  endfunction
  let s:random = get(s:, 'random', 'dev_unix')
endif

if lh#os#system_detected() == 'windows'
  " This flavour is very slow, and best avoided!
  function! lh#common#rand_unix(max)
    let nb_bytes = float2nr(log(a:max-1)/s:k_lg256)+1
    let rnd = matchstr(system('echo %RANDOM%'), '\d\+')
    let res = rnd % a:max
    return res
  endfunction
  let s:random = get(s:, 'random', 'dev_windows')
endif

if filereadable('/dev/urandom') && has('float')
  " This flavour is very slow!
  let s:k_lg256 = log(256)
  function! lh#common#rand_dev_urandom(max)
    let nb_bytes = float2nr(log(a:max-1)/s:k_lg256)+1
    let rnd = matchstr(system('od -A n -t d -N '.nb_bytes.' /dev/urandom'), '\d\+')
    let res = rnd % a:max
    return res
  endfunction
  let s:random = get(s:, 'random', 'dev_urandom')
endif

function! lh#common#rand(max)
  call lh#assert#value(s:).has_key('random')
  return lh#common#rand_{s:random}(a:max)
endfunction
2
  • Thanks for your answer Luc. My question was really about why didn't Vim provided a random number generator function but we also have How to generate random numbers? which would benefit from your portable function :)
    – statox
    Oct 19 '20 at 15:32
  • 1
    Ah. I knew I did not invent all my solutions. ^^' Back to you question, things are added by people that need such features. If nobody is motivated enough to introduce a random generator in Vim, well... We won't have it. Same thing with reduce(), find_if() , tsort(), zip() ... Oct 19 '20 at 16:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.