Page 3 of 7

Re: User Functions Exchange

Posted: 03 May 2015 16:14
by binocular222
nerdweed wrote:Is there any default include file which is always loaded? If not, how can we call UDF's from outside (like AHK) as the include wouldn't work in a one liner and without the include statement, the function definition is unknown.
See my wish here http://www.xyplorer.com/xyfc/viewtopic. ... 90#p122589, seems everyone ignored it

Re: User Functions Exchange

Posted: 03 May 2015 17:30
by SammaySarkar
The include command still requires a complete line for itself.
But wait, the function cmd also had this requirement, but it was lifted last ver. Good things come to those who wait... ... good things, including inline include.

Re: User Functions Exchange

Posted: 07 May 2015 08:30
by highend

Code: Select all

// Convert a roman literal into an arabic number
// Derived from http://rosettacode.org/wiki/Roman_numerals/Decode#AutoHotkey
function romanToArabic($roman) {
    if (!$roman || !regexmatches(trim($roman, " <tab>"), "^(M{0,4})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$")) { return 0; }

    $result = 0;
    while ($i++ < strlen($roman)) {
        $curChar = recase(substr($roman, $i - 1, 1));
        $curNum  = replacelist($curChar, "i,v,x,l,c,d,m", "1,5,10,50,100,500,1000", ",");

        if ($curNum > $oldNum && $oldNum) {
            $result = $result - 2 * $oldNum;
        }
        $result = $result + $curNum;
        $oldNum = $curNum;
    }
    return $result;
}

// Convert an arabic number into a roman literal
// Returns an empty string if the number is not in range (1-3999)
// Derived from http://rosettacode.org/wiki/Roman_numerals/Encode#AutoHotkey
function arabicToRoman($number) {
    $romans   = "M,CM,D,CD,C,XC,L,XL,X,IX,V,IV,I";
    $decimals = "1000,900,500,400,100,90,50,40,10,9,5,4,1";

    $number = trim($number, " <tab>");
    if (!$number || $number > 3999 || !regexmatches($number, "^\d+$")) { return; }

    $result = "";
    $i = 1;
    foreach($roman, $romans, ",") {
        $decimal = gettoken($decimals, $i, ",");
        while ($number >= $decimal) {
            $result = $result . $roman;
            $number = $number - $decimal;
        }
        $i++;
    }
    return $result;
}

Re: User Functions Exchange

Posted: 07 May 2015 20:35
by highend
A minor (but in my daily workflow an important) one...

regexescape(): Escape a string for a regexmatch / -replace

Code: Select all

function regexEscape($string) {
    return regexreplace($string, "([\\^$.+*|?(){\[])", "\$1");
}

Code: Select all

text regexEscape("C:\Temp\Mix.Song (One $) + [OLD] {delete it}.mp3");
// Output: C:\\Temp\\Mix\.Song \(One \$\) \+ \[OLD] \{delete it}\.mp3

Re: User Functions Exchange

Posted: 08 May 2015 11:01
by SammaySarkar
And here's a similar one I have to use often.

Escapes special html characters in a string, so the string can be faithfully displayed in a html page.

Code: Select all

FUNCTION htmlescape($str, $crlf = 0) {
/* Escape special html [markup] characeters in a string,
 * to display it correctly in an html page.
 *  $str   the string to Escape
 *  $crlf  escape linebreaks (with <br>) */
  // & has to be the 1st replacement
  $str = replace($str, '&', '&');
  $str = replace($str, '<', '<');
  $str = replace($str, '>', '>');
  $str = replace($str, '"', '"');
  // escape consecutive spaces. This can be "freezy", so disabled
  // $str = regexreplace($str, ' (?= )', '&nbsp;');
  if ($crlf == 1) { $str = replace($str, <crlf>, '<br>'); }
  return $str;
}

The original, slower version is below.
replacelist() is far slower than separate replace() calls.

Code: Select all

FUNCTION htmlescape($str, $crlf = 0) {
/* Escape special html [markup] characeters in a string,
 * to display it correctly in an html page.
 *  $str   the string to Escape
 *  $crlf  escape linebreaks (with <br>) */
  // & has to be the 1st replacement
  $str = replacelist($str,'&|<|>|"', '&|<|>|"', '|');
  // escape consecutive spaces. This can be "freezy", so disabled
  // $str = regexreplace($str, ' (?= )', '&nbsp;');
  if ($crlf == 1) { $str = replace($str, <crlf>, '<br>'); }
  return $str;
}
[/size]

Re: User Functions Exchange

Posted: 16 May 2015 07:13
by SammaySarkar
allstrpos(haystack, needle, start, matchcase, rel) : Returns all positions of a substring, separated by pipe.

The Return is similar to regular strpos(), but for negative start position. Details in function comment.
function

Code: Select all

function allstrpos($haystack, $needle, $start = 0, $matchcase = 0, $rel = 0){
 /* Return |-separated positions of all occurrences of $needle in $haystack.
 **  $haystack      string to search in
 **  $needle        string to search for
 **  [$start=0]     0-based start position to start search
 **                 if negative, search is started from end, omitting abs($start) chars
 **                 **NOTE**: $start = '-' starts searching from end, without omission
 **                           0 : start from lefmost char, - : start from rightmost char
 **  [$matchcase=0] case-sensitivity of search. Any value other than 0 is accepted as 1.
 **  [$rel=0]       return position relative to $start. Any value other than 0 is 1.
 **                 if $start is negative, positions are returned from end
 ** Returns -1 on no match, or '' on errors -- errors returned in global $G_UDF_lasterr
 */
  //vErrodication -- this section can be removed safely if input validity is ensured.
    global $G_UDF_lasterr;
    if($haystack=='')||($needle=='')||(($start!='-')&&(($start!=0))&&($start*1==0))
    {$r=='';$G_UDF_lasterr=$G_UDF_lasterr.'allstrpos()::BADPARAM;';}if($r==''){return $r;}
  //^Errodication

  if ($start > strlen($haystack)) { return '-1' ; } //ends function
  $return = ''; $inv = 0; $len = 0; $start = ($start == '-') ? $start : round($start) ;
  if ($start<0||$start=='-') { $haystack = substr($haystack,, ($start=='-')?'':$start);
                               $inv = 1; $start = 0; $len = strlen($haystack);         }
  $matchcase = (round($matchcase) != 0) ? 1 : 0 ;
  $rel = (round($rel) == 1) ? ($start + $len - $inv) : 0 ;
  $pos = strpos($haystack, $needle, $start, $matchcase) ;
  if ($pos != -1) {
    $ndlen = strlen($needle) ;
    while ($pos != -1) {
      $return = $return . abs(($pos - $rel)) . '|' ;
      $pos    = strpos($haystack, $needle, ($pos + $ndlen), $matchcase) ;
    }
  } else { $return = -1 ; }
  if ($inv == 1) { $fmt = 've'; } else { $fmt = 'e'; }
  $return = formatlist($return, $fmt);
  return $return ;
}
call (assuming allstrpos() is saved in <xyscripts>\inc\allstrpos.xyi)

Code: Select all

INCLUDE "inc\allstrpos.xyi"
"allstrpos test"
  text allstrpos('3.1415', '1'); //2|4
  text allstrpos('3.1415', '1','-',,1); //1|3
  text allstrpos('abcdef','def',2,,1); //1

Re: User Functions Exchange

Posted: 04 Jun 2015 19:35
by highend

Code: Select all

/* *****************************************************************
 * Returns the index of one / multiple tab(s)
 * Returns "0" if no index is found

 * Parameter(s):
 *   $mode: c (caption = default), p (path), n (name), d (data)
 *   $side: l (left = default), r (right) -> Count from left / right side on
 *   $ret : f (first only = default), a (all) -> Which index to return
 *   $sep : "|" (default) -> Separator for multiple indexes

 * Example(s):
 *   list of tab captions = "D:\|C:\|F:\|C:\"
 *   text getTabIndex("C:\", "c", "r", "a");
 *   Output: 4|2

 *   text getTabIndex("C:\");
 *   Output: 2
***************************************************************** */
function getTabIndex($str, $mode="c", $side="l", $ret="f", $sep="|") {
    if !($str) { return 0; }
    $mode = replacelist($mode, "c|d|p|n", "caption|data|path|name", "|");

    $tabCount = tab("get", "count");
    if ($side == "l")     { $i = 1; $op = "+"; $cmp = ">"; $cmpVal = $tabCount;  }
    elseif ($side == "r") { $i = $tabCount; $op = "-"; $cmp = "<="; $cmpVal = 0; }

    $retVal = "";
    while (true) {
        if (eval($i $cmp $cmpVal)) { break; }
        if ($str LikeI tab("get", $mode, $i)) {
            if ($ret == "f")     { return $i; }
            elseif ($ret == "a") { $retVal = $retVal . $i . $sep; }
        }
    $i = eval($i $op 1);
    }
    return ($retVal) ? trim($retVal, $sep, "R") : 0;
}

Re: User Functions Exchange

Posted: 06 Jun 2015 19:47
by SammaySarkar
a simple one:
swap($a, $b). Swaps the values of $a and $b. This has no return data but modifies the passed variables. Think of it as a command when using.

Code: Select all

FUNCTION swap(&$a, &$b) { $c = $a; $a = $b; $b = $c; unset $c; }
call

Code: Select all

$a=1;$b=2; swap($a,$b); echo '$a is now: '.$a.<crlf>.'$b is now: '.$b ;
Why? hide those menial steps. :whistle:

Re: User Functions Exchange

Posted: 10 Jun 2015 12:00
by highend
Sometimes I want to get a range of tokens (e.g. 2-5) from a string...

Code: Select all

/* *****************************************************************
 * Returns a range of tokens
 * Returns an empty string if your values are out of bounds

 * Parameter(s) are the same as in the original gettoken() function
 * Two exceptions:
 *   There is no $flag parameter. It's autoset to 2 internally
 *   $count: 1 = default -> This defines how many tokens you want to return

 * Example(s):
 *   $tokens = "C:\|D:\|E:\|F:\|G:\";
 *   text getTokenRange($tokens, 2, 3, "|");
 *   Output: D:\|E:\|F:\

 *   text getTokenRange($tokens, 3, , "|"); // Equals original function
 *   Output: E:\
***************************************************************** */
function getTokenRange($str, $index=1, $count=1, $sep=" ", $format="") {
    if ($count > 1) {
        $all = gettoken($str, $index, $sep, $format, 2);
        $rem = regexreplace($sep . gettoken($str, $index + $count, $sep, $format, 2), "([\\^$.+*|?(){\[])", "\$1");
        return regexreplace($all, $rem . "$");
    }
    return gettoken($str, $index, $sep, $format);
}

Re: User Functions Exchange

Posted: 11 Jun 2015 08:02
by waqu
Convert milliseconds to hh:mm:ss.fff

Code: Select all

function mstime ($ms) {
  $fff = $ms % 1000;
  $ms = $ms \ 1000;
  $ss = $ms % 60;
  $ms = ($ms - $ss) / 60;
  $mm = $ms % 60;
  $hh = ($ms - $mm) / 60;
  $hh = format ("$hh", "00");
  $mm = format ("$mm", "00");
  $ss = format ("$ss", "00");
  $fff = format ("$fff", "000");
  return "$hh:$mm:$ss.$fff";
 }

Re: User Functions Exchange

Posted: 11 Jun 2015 11:05
by PeterH
I think instead of $in=($in - $ms) / 1000; it would be shorter to say $in=$in \ 1000;
(Integer division, cuts fractions, no rounding.)

I think

Code: Select all

 $ms = $in % 1000;
 $in = $in \ 1000;
even looks better :D

Re: User Functions Exchange

Posted: 14 Jun 2015 18:37
by SammaySarkar
in() : checks whether a token (or tokens) exists in a tokenlist. Returns either 1 or 0. (Intended for conditionals)

function code

Code: Select all

FUNCTION in($token, $tokenlist, $separator='|', $matchcase=0) {
  /*Returns 1 if $token exists in $tokenlist,  else returns 0
  ** PARAMTERS
  **  token      token to search.  Can itself be a list
  **             of tokens, separated by $separator.
  **  tokenlist  the list of tokens to search in
  **  separator  separator between tokens in tokenlist
  **  matchcase  0=case-insensitive, 1=case-sensitive
  **
  ** RETURN
  ** 1 if $token if found, 0 otherwise
  **
  ** NOTE
  ** if $token itself is a token list, then each of it's
  ** "subtokens" are searched, and if all is found, then
  ** return is 1; if even one is not found, return is 0.
  */

  if (strpos($token, $separator)==-1){
    $return = gettokenindex($token, $tokenlist, $separator,
                            (($matchcase==1) ? '' : 'i') .'');
  }
  else {
    foreach ($atoken, $token, $separator) {
      $return = gettokenindex($atoken, $tokenlist, $separator,
                              (($matchcase==1) ?'' :'i') . '');
      if ($return == 0){ break; } //no match, stop, return 0
    }
  }
  return ($return>0)?1:0;
}
call
assuming the function is saved in <xyscripts>\inc\in.xyi

Code: Select all

INCLUDE 'inc\in.xyi';
"in() test"
  $token = 'bat'; $tokens = 'cmd|bat|sh|vbs|wsh';
  if (in($token, $tokens)==1) {echo "it's all in tokens!"; }
  $token = 'bat|cpp'; $tokens = 'cmd|bat|sh|vbs|wsh';
  if (in($token, $tokens)==0) {echo "it's not all in tokens!"; }


here's an alternative lightweight in() that does not try to match multiple tokens in $token

Code: Select all

//lightweight in(), no mulittoken
FUNCTION in($token, $tokenlist, $separator='|', $matchcase=0){
  /*Returns 1 if $token exists in $tokenlist,  else returns 0*/
  $return = gettokenindex($token, $tokenlist, $separator,
                          (($matchcase==1) ? '' : 'i') .'');
  return ($return>0)?1:0;
}

Re: User Functions Exchange

Posted: 02 Jul 2015 00:01
by Marco

Code: Select all

function sqrt($num) {
// Returns the square root of a positive number in base 10.
// Any other argument causes the function to throw an error.
// The result has machine precision.
// Based on the Babylonian algorithm (see https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method).
//
//   $num   the positive number in base 10 to calculate the square root of

 assert regexmatches("$num", "[^0-9.,]") == "", "Argument is not a positive number";

 $num = eval($num);

 if ($num >= 100) {
  $root = $num/20;
 } elseif ($num >= 2) {
  $root = $num/2;
 } elseif ($num >= 2/100) {
  $root = $num;
 } else {
  $root = $num*10;
 };

 while ($root != $root_prev) {
  $root_prev = $root;
  $root = ($root + $num/$root)/2;
 };

 return $root;
}

Re: User Functions Exchange

Posted: 02 Jul 2015 00:53
by highend
Don't get me wrong Marco but imho this can be solved in a (probably) more elegant way.
I left out your assert & regexmatches command (because I hope a user is intelligent enough to feed it with a correct value):

Or, did I miss anything (which should be obvious)?

Code: Select all

function sqrt($num) { return eval($num) ^ (1/2); }

Re: User Functions Exchange

Posted: 02 Jul 2015 00:59
by Marco
Oh. My. God. I never thought of that :oops: :oops: :oops: Shame on me, time for some corner time... :oops: :oops: :oops:
(But the eval was necessary because feeding a decimal number with a dot or a comma made a difference)