writeup.txt 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. [size=5][color=red][b][u][color=green]Introduction[/color][/u][/b][/color][/size]
  2. [size=5][color=red][b][u][color=green]Concepts[/color][/u][/b][/color][/size]
  3. There are several concepts to understand before using this library, they are common in functional programming circles, but PAWN isn't functional programming. Basically, they are just odd names for common structures. I'm not going to explain WHY things have these names, there are reasons though.
  4. [list][*][size=2][color=red][b][u][color=green]Map[/color][/u][/b][/color][/size][/list]
  5. A "map" is when you do the same thing to every element of an array: for example increase them all by 1, or print them all. In SA:MP this is usually done as a loop, the example below will "map [b]+7[/b] over [b]array[/b]":
  6. [pawn]
  7. new
  8. array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
  9. dest[sizeof (array)];
  10. for (new i = 0; i != sizeof (array); ++i)
  11. {
  12. dest[i] = array[i] + 7;
  13. }
  14. [/pawn]
  15. Using "y_functional", instead you pass an expression to run for every element of the array, an array to run the code on, and an array to store the results in:
  16. [pawn]
  17. Map({_0 + 7}, array, dest);
  18. [/pawn]
  19. The syntax is not too difficult to understand, but still not entirely obvious. The code between the braces is what is run for every element of the array. The variable called "_0" is the contents of the current array index. Think of it like this:
  20. [pawn]
  21. for (new i = 0; i != sizeof (array); ++i)
  22. {
  23. new
  24. _0 = array[i],
  25. result = (_0 + 7); // This is the part you specify.
  26. dest[i] = result;
  27. }
  28. [/pawn]
  29. What if you want to modify the input array, instead of writing the results to a different array? This is simple - just give the same array as the input and output:
  30. [pawn]
  31. Map({_0 + 7}, array, array);
  32. [/pawn]
  33. What if you don't actually want to store the results? Say if you want to print every element of the array, but do no processing? This again is simple:
  34. [pawn]
  35. Map_({printf("%d", _0)}, array);
  36. [/pawn]
  37. The "_" suffix means that there is nothing to return.
  38. What if you want to know the current index, as well as the current value? For this there are the "MapIdx" functions:
  39. [pawn]
  40. MapIdx_({printf("array[%d] = %d", _0, _1)}, array); // Note that the index is "_0".
  41. MaxIdx({_1 / _0}, array, dest);
  42. [/pawn]
  43. [list][*][size=2][color=red][b][u][color=green]Fold[/color][/u][/b][/color][/size][/list]
  44. A "fold" converts an array to a single value in some way, for example by adding up all the values. A "fold" uses all the elements of the array to get the final value, and again is normally done via a loop with an extra variable. The total (sum) example would be:
  45. [pawn]
  46. new
  47. array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
  48. total = 0;
  49. for (new i = 0; i != sizeof (array); ++i)
  50. {
  51. total = total + array[i];
  52. }
  53. [/pawn]
  54. This becomes:
  55. [pawn]
  56. new
  57. total = FoldL({_0 + _1}, 0, array);
  58. [/pawn]
  59. Unlike "Map" there is no destination array since we are only storing one value. "_0" is the running total, "_1" is the current array cell. The second function parameter, "0", is the initial value of the loop. For the product (what you get when you multiply every value up), the initial value must be "1" since "0 * x = 0" always:
  60. [pawn]
  61. new
  62. total = FoldL({_0 * _1}, 1, array);
  63. [/pawn]
  64. [list][*][size=2][color=red][b][u][color=green]FoldL and FoldR[/color][/u][/b][/color][/size][/list]
  65. Both of the examples above used "FoldL" - the "L" stands for "Left", but the "Right" version would have worked two, so what is the difference?
  66. Consider this example:
  67. [pawn]
  68. new
  69. array[] = {5, 10, 4};
  70. total = FoldL({_0 / _1}, 1000, array);
  71. [/pawn]
  72. This evaluates as:
  73. [pawn]
  74. new
  75. total = (((1000 / 5) / 10) / 4); // = 5
  76. [/pawn]
  77. Because "/" is not commutative, this is a different sum:
  78. [pawn]
  79. new
  80. total = (1000 / (5 / (10 / 4))); // = 500
  81. [/pawn]
  82. They are the same numbers in the same written order, but give different results according to the evaluation order.
  83. [list][*][size=2][color=red][b][u][color=green]Scan[/color][/u][/b][/color][/size][/list]
  84. A "scan" is like a "fold", but keeps all the intermediate results as well. To demonstrate this I'll use a more complete "sum" example:
  85. [pawn]
  86. new
  87. array[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
  88. scan[sizeof (array) + 1]. // + 1.
  89. total = 0;
  90. for (new i = 0; i != sizeof (array); ++i)
  91. {
  92. scan[i] = total;
  93. total = total + array[i];
  94. }
  95. scan[sizeof (array)] = total;
  96. [/pawn]
  97. After this code "total" is 10 (equal to adding up 10 1s), the array "scan" becomes:
  98. [code]
  99. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  100. [/code]
  101. Another example:
  102. Input:
  103. [code]
  104. array = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  105. [/code]
  106. Output:
  107. [code]
  108. scan = { 0, 0, 1, 3, 6, 10, 15, 21, 28, 36, 45};
  109. [/code]
  110. Just to demonstrate how this came about:
  111. [code]
  112. scan = {
  113. 0,
  114. 0 + 0,
  115. 0 + 0 + 1,
  116. 0 + 0 + 1 + 2,
  117. 0 + 0 + 1 + 2 + 3,
  118. 0 + 0 + 1 + 2 + 3 + 4,
  119. 0 + 0 + 1 + 2 + 3 + 4 + 5,
  120. 0 + 0 + 1 + 2 + 3 + 4 + 5 + 6,
  121. 0 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7,
  122. 0 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8,
  123. 0 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
  124. };
  125. [/code]
  126. Note that "0" appears twice in the output because that is both the initial value of "total" AND the first value in the input array.
  127. [list][*][size=2][color=red][b][u][color=green]Filter[/color][/u][/b][/color][/size][/list]
  128. A "filter" finds all arrays elements that meet some value. For example, to get all the positive values in an array:
  129. [pawn]
  130. new
  131. array[10] = {1, -2, 4, -8, 16, -32, 64, -126, 256, -512},
  132. dest[sizeof (array)],
  133. count;
  134. for (new i = 0; i != sizeof (array); ++i)
  135. {
  136. if (array[i] > 0)
  137. {
  138. dest[count++] = array[i];
  139. }
  140. }
  141. [/pawn]
  142. To check which array elements are connected players:
  143. [pawn]
  144. new
  145. array[10] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90},
  146. dest[sizeof (array)],
  147. count;
  148. for (new i = 0; i != sizeof (array); ++i)
  149. {
  150. if (IsPlayerConnected(array[i]))
  151. {
  152. dest[count++] = array[i];
  153. }
  154. }
  155. [/pawn]
  156. Here "count" returns the number of elements found, and "dest" includes them all.
  157. [list][*][size=2][color=red][b][u][color=green]Zip[/color][/u][/b][/color][/size][/list]
  158. A "zip" combines two or more arrays in to one. This example simply adds them:
  159. [pawn]
  160. new
  161. array1[10] = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90},
  162. array2[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
  163. dest[sizeof (array1)],
  164. count;
  165. for (new i = 0; i != sizeof (array); ++i)
  166. {
  167. dest[i] = array1[i] + array2[i];
  168. }
  169. [/pawn]
  170. [size=5][color=red][b][u][color=green]Combinations[/color][/u][/b][/color][/size]
  171. These functions can be quite quickly built up to do complex code. You could use a "zip" and a "fold" to find the distance between two points. The normal code for this is:
  172. [pawn]
  173. DistanceBetweenPoints(Float:p0[3], Float:p1[3])
  174. {
  175. // Get the difference in each dimension.
  176. p0[0] -= p1[0];
  177. p0[1] -= p1[1];
  178. p0[2] -= p1[2];
  179. // Square it.
  180. p0[0] *= p0[0];
  181. p0[1] *= p0[1];
  182. p0[2] *= p0[2];
  183. // Add them.
  184. new
  185. Float:sq = p0[0] + p0[1] + p0[2];
  186. // Square root.
  187. return floatsqroot(sq);
  188. }
  189. [/pawn]
  190. With y_functional:
  191. [pawn]
  192. DistanceBetweenPoints(Float:p0[3], Float:p1[3])
  193. {
  194. // Get the difference in each dimension.
  195. ZipWith({_0 - _1}, p0, p1, p0);
  196. // Square it.
  197. Map({_0 * _0}, p0, p0);
  198. // Add them.
  199. new
  200. Float:sq = Sum(p0);
  201. // Square root.
  202. return floatsqroot(sq);
  203. }
  204. [/pawn]
  205. Or combining several steps:
  206. [pawn]
  207. DistanceBetweenPoints(Float:p0[3], Float:p1[3])
  208. {
  209. // Get the difference in each dimension.
  210. ZipWith({_0 - _1}, p0, p1, p0);
  211. // Square it and add them.
  212. new
  213. Float:sq = FoldL({_0 + _1 * _1}, 0, p0);
  214. // Square root.
  215. return floatsqroot(sq);
  216. }
  217. [/pawn]