summaryrefslogtreecommitdiffstats
path: root/nselib/listop.lua
blob: 7dd8386ea2640ba4574866931402077910bf1613 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
---
-- Functional-style list operations.
--
-- People used to programming in functional languages, such as Lisp
-- or Haskell, appreciate their handling of lists very much. The
-- <code>listop</code> module tries to bring much of the functionality from
-- functional languages to Lua using Lua's central data structure, the table, as
-- a base for its list operations. Highlights include a <code>map</code>
-- function applying a given function to each element of a list.
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html

local stdnse = require "stdnse"
local table = require "table"
_ENV = stdnse.module("listop", stdnse.seeall)

--[[
--
Functional programming style 'list' operations

    bool  is_empty(list)
    bool  is_list(value)

    value apply(function, list)
    list  map(function, list)
    list  filter(function, list)
    list  flatten(list)
    list  append(list1, list2)
    list  cons(value1, value2)
    list  reverse(list)

    value car(list)
    value ncar(list, x)
    list  cdr(list)
    list  ncdr(list, x)

    where 'list' is an indexed table
    where 'value' is an lua datatype
--]]

--- Returns true if the given list is empty.
-- @param l A list.
-- @return True or false.
function is_empty(l)
  return #l == 0 and true or false;
end

--- Returns true if the given value is a list (or rather a table).
-- @param l Any value.
-- @return True or false.
function is_list(l)
  return type(l) == 'table' and true or false;
end

--- Calls <code>f</code> for each element in the list. The returned list
--contains the results of each function call.
-- @usage
-- listop.map(tostring,{1,2,true}) --> {"1","2","true"}
-- @param f The function to call.
-- @param l A list.
-- @return List of function results.
function map(f, l)
  local results = {}
  for _, v in ipairs(l) do
    results[#results+1] = f(v);
  end
  return results;
end

--- Calls the function with all the elements in the list as the parameters.
-- @usage
-- listop.apply(math.max,{1,5,6,7,50000}) --> 50000
-- @param f The function to call.
-- @param l A list.
-- @return Results from <code>f</code>.
function apply(f, l)
  return f(table.unpack(l))
end

--- Returns a list containing only those elements for which a predicate
-- function returns true.
--
-- The predicate has to be a function taking one argument and returning
-- a Boolean. If it returns true, the argument is appended to the return value
-- of filter.
-- @usage
-- listop.filter(isnumber,{1,2,3,"foo",4,"bar"}) --> {1,2,3,4}
-- @param f The function.
-- @param l The list.
-- @return Filtered list.
function filter(f, l)
  local results = {}
  for i, v in ipairs(l) do
    if(f(v)) then
      results[#results+1] = v;
    end
  end
  return results
end

--- Fetch the first element of a list.
-- @param l The list.
-- @return The first element.
function car(l)
  return l[1]
end

--- Fetch all elements following the first in a new list.
-- @param l The list.
-- @return Elements after the first.
function cdr(l)
  return {table.unpack(l, 2)}
end

--- Fetch element at index <code>x</code> from <code>l</code>.
-- @param l The list.
-- @param x Element index.
-- @return Element at index <code>x</code> or at index <code>1</code> if
-- <code>x</code> is not given.
function ncar(l, x)
  return l[x or 1];
end

--- Fetch all elements following the element at index <code>x</code>.
-- @param l The list.
-- @param x Element index.
-- @return Elements after index <code>x</code> or after index <code>1</code> if
-- <code>x</code> is not given.
function ncdr(l, x)
  return {table.unpack(l, x or 2)};
end

--- Prepend a value or list to another value or list.
-- @param v1 value or list.
-- @param v2 value or list.
-- @return New list.
function cons(v1, v2)
  return{ is_list(v1) and {table.unpack(v1)} or v1, is_list(v2) and {table.unpack(v2)} or v2}
end

--- Concatenate two lists and return the result.
-- @param l1 List.
-- @param l2 List.
-- @return List.
function append(l1, l2)
  local results = {table.unpack(l1)}

  for _, v in ipairs(l2) do
    results[#results+1] = v;
  end
  return results
end

--- Return a list in reverse order.
-- @param l List.
-- @return Reversed list.
function reverse(l)
  local results = {}
  for i=#l, 1, -1 do
    results[#results+1] = l[i];
  end
  return results
end

--- Return a flattened version of a list. The flattened list contains
-- only non-list values.
-- @usage
-- listop.flatten({1,2,3,"foo",{4,5,{"bar"}}}) --> {1,2,3,"foo",4,5,"bar"}
-- @param l The list to flatten.
-- @return Flattened list.
function flatten(l)
  local function flat(r, t)
    for i, v in ipairs(t) do
      if(type(v) == 'table') then
        flat(r, v)
      else
        table.insert(r, v)
      end
    end
    return r
  end
  return flat({}, l)
end

return _ENV;