In this article, I will introduce the higher-order function provided by Nim and compare it with Python. It also includes tips on how to write Nim.
*** Efficient, expressive and elegant Nim is a statically typed compiled system programming language. It combines the successful concepts of mature languages such as Python, Ada, and Modula. *** *** (Quote: https://nim-lang.org/ with Google Translate) In short, it has a syntax like Python and is a programming language as fast as C language. It's a dream language for me, who usually writes Python but is dissatisfied with its speed.
A higher-order function is a function *** that takes a *** function as an argument. It was used in LISP and ML languages in the old days, but it has been implemented in many programming languages due to the rise of functional languages in recent years. For example, it has been implemented in scripting languages such as Python, and more recently in JavaScript (since ECMAScript 2015). It seems that it can be written in Rust and Go, but I am not familiar with it so I will omit it.
Introducing higher-order functions and their templates implemented in Nim's sequtil module.
map
map
proc map[T, S](s:openArray[T];op:proc(x:T):S{...}): seq[S] {...}
map
takes an array s
of type ʻopenArray and a procedure ʻop
as arguments, and returns seq
with ʻop applied to each element of
s. ʻOpenArray
is a general-purpose type that indicates an array, and includes sequences
, string
, and ʻarray`.
map example
import sequtils
let #This is an immutable variable declaration
s = [1,2,3,4] #This is an Array type
op = proc (x: int): int = x * x #Anonymous function, a function that returns the square of an argument
echo s.map(op) # => @[1, 4, 9, 16] ##Each element of s is squared
echo s # => [1, 2, 3, 4] ##The original array does not change
echo s.map(op) == map(s, op) # => true
##Nim Tips (How to call a procedure)
## p(x)And x.p()Is equivalent
apply
apply
proc apply[T](s:varopenArray[T];op:proc(x:T):T{...}) {...}
ʻApply takes an array
s of type ʻopenArray
and a procedure ʻop as arguments, and applies ʻop
to each element of s
. Similar to map
, but ʻapply has no return value and modifies
s` directly. Actually, there is another apply, but omitted.
apply example
import sugar #Experimental module
var #mutable variable declaration
x = [true, false, false, true, true]
echo x #=> [true, false, false, true, true]
apply(x, b => not b) #Take not of each element of x
echo x #=> [false, true, true, false, false]
##Nim Tips (How to write anonymous functions)
## b =>not b is an abbreviation for anonymous functions. The left side is the argument and the right side is the return value
##However, import sugar is required
filter
filter
proc filter[T](s:openArray[T];pred:proc(x:T):bool{...}): seq[T] {...}
filter
takes an array s
of type ʻopenArray and a procedure
predas arguments, and returns
seq consisting of elements of
swhere
pred returns
true`.
filter example
import strutils
let
str = "happY holidAYs"
upperStr = str.filter(s => s.isUpperAscii()) #Filter uppercase
echo str #=> "happY holidAYs"
echo upperStr #=> @['Y', 'A', 'Y']
echo upperStr.join("") #=> "YAY" ##Combine elements of a string type array(Same as Python)
##Nim Tips (naming)
##It is recommended to declare Nim variables and procedures in camelCase.
keepIf
keepIf
proc keepIf[T](s:varseq[T];pred:proc(x:T):bool{...}) {...}
keepIf
takes s
of type seq
and procedure pred
as arguments, and leaves only the elements that pred
returns true
in s
. However, note that the argument is seq
instead of ʻopenArray`.
These are the map, apply, filter and keepIf
templates, respectively. With these, you can easily (just a little) call higher-order functions. Since the usage is almost the same, I will introduce an example of mapIt
.
mapIt example
let
v = [1,2,3,4,5]
w = v.mapIt(it*2) #it means each element of v, which means to double each element
echo w # => @[2, 4, 6, 8, 10]
point
--The arguments of mapIt
(and so on) are arrays and ** expressions **
--ʻItin the template is each element of the array of arguments --The return value is
seq` like the original function
Python also implements map, filter, reduce (corresponding to foldl, foldr), but there is no equivalent to apply or keepIf (pandas has it). Therefore, if you want to use the list after multiplying the list l
by map
,
l = [1, 2, 3, 4] #I want to double each value
mapped_l = map(lambda x: x*2, l) # mapped_l is not a list
assert type(mapped_l) is map # => true
mapped_l = list(mapped_l) #Return to list
Must be. The return type of map
is map object
, which is different from the argument. This is a generator, so if you want to use it as the original type, you have to explicitly return it. This is the same for filter
, and the return value of filter
is filter object
. For example
l = [-3, -2, -1, 0, 1, 2, 3]
p = filter(lambda x: x>0, l) #Leave only positive elements
len(p) #Error because p is a filter object
len(list(p)) #This is okay. The answer is 3
len([li for li in l if li > 0]) #This is also okay. Is this a Python-like way of writing? ?? ??
I personally don't like this very much. In the case of Nim, the return type of l.map (proc ...)
is seq
, so it is easy to use because there is no need for such processing (personal impression).
I summarized the higher-order functions of Nim. Nim is still a developing language, but I hope it will develop further in the future. I hope it helps you a little: upside_down:
Recommended Posts