LINUX.ORG.RU

[C++][критика]Отругайте реализацию задачки

 ,


0

1

Началось с того, что смотрел вакансии на одном сайте. Увиденная задачка заинтересовала и заставила вспомнить былое. Вакансия, к слову, из ДС. Стало интересно, насколько мои высушенные мозги годятся для дела в нормальных конторах.

Есть последовательность идентификаторов, строящаяся по особым правилам: 1. Первый идентификатор последовательности имеет вид «A1». Второй - «A2», третий - «A3» и так далее.

За «A9» следующий - «B1». Следующий после «Z9» имеет вид «A1-A1», потом «A1-A2» и так далее.

После «A1-Z9» следующим идет «A2-A1».

2. Максимальная длина идентификатора - десять групп по два символа. 3. В идентификаторах никогда не должны присутствовать буквы «D», «F», «G», «J», «M», «Q», «V», и цифра «0».

Нужно: на C++ написать библиотечный класс, предназначенный для использования другими программистами.

Функция должна получать в качестве входного параметра строку с идентификатором из описанной последовательности, и генерировать на выходе строку, содержащую следующий идентификатор последовательности. Например, функция получает «A1-Z9» и возвращает «A2-A1».

Вот моя реализация, хочу услышать здравую критику грамотного народа. Судя по наблюдениям, здесь такой встречается.

main.cpp

#include <iostream>
#include <cstring>

#include "cmeganumber.h"

using namespace std;

int main(int argc, char** argv)
{
    CMegaNumber t;
    t = "C9-Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z9";
    cout << "baseval = " << t.szvalue() << endl;
    cout << "newval = " << (++t).szvalue() << ", overflow = " << t.m_bOverflow << endl;
    return 0;
}
cmeganumber.h
#ifndef CMEGANUMBER_H_INCLUDED
#define CMEGANUMBER_H_INCLUDED

class CMegaNumber
{
    private:
        char m_lpszValue[30];
        bool m_bAssigned;
    public:
        bool m_bOverflow;
        CMegaNumber();
        CMegaNumber& operator=(const char * lpszValue);
        CMegaNumber& operator++();
        char * szvalue() { return m_lpszValue; };
};

#endif // CMEGANUMBER_H_INCLUDED
cmeganumber.cpp
#include <cstring>

#include "cmeganumber.h"

// CMegaNumber implementation
/* constructor
    */
CMegaNumber::CMegaNumber()
{
    m_bOverflow = false;
    m_bAssigned = false;
    memset(m_lpszValue, 0, 30);
}
/* assignment operator overload
    */
CMegaNumber& CMegaNumber::operator=(const char * lpszValue)
{
    if(30 <= strlen(lpszValue))
    {
        strcpy(m_lpszValue, "err-too-long");
        m_bAssigned = false;
        return *this;
    }
    strcpy(m_lpszValue, lpszValue);
    m_bAssigned = true;
    return *this;
}
/* pre-increment operator overload
    */
CMegaNumber& CMegaNumber::operator++()
{
    if(!m_bAssigned)
    {
        strcpy(m_lpszValue, "err-not-assigned");
        return *this;
    }
    bool bIncHigh = false;
    char * p = m_lpszValue + strlen(m_lpszValue) - 1;
    *p == '9' ? *p = '1', bIncHigh = true : (*p)++;
    p--;
    if(bIncHigh)
    while(p >= m_lpszValue && bIncHigh)
    {
        switch(((unsigned long)p - (unsigned long)m_lpszValue) % 3)
        {
            case 1:
            *p == '9' && bIncHigh ? *p = '1' : ((*p)++, bIncHigh = false);
            break;
            
            case 0:
            *p == 'Z' && bIncHigh ? *p = 'A' : ((*p)++, bIncHigh = false);
            while(*p == 'D' ||
                  *p == 'F' ||
                  *p == 'G' ||
                  *p == 'J' ||
                  *p == 'M' ||
                  *p == 'Q' ||
                  *p == 'V') (*p)++;
            break;
            
            case 2:;
        }
        p--;
    }
    m_bOverflow = bIncHigh;
    return *this;
}

★★

ышшо один вариант :) Просто С, но по крайней мере наборы символов не вшиваются жестко в код, а задаются из вне функции.

#include <stdio.h>
#include <string.h>

char hi[]="ABCEHIKLNOPRSTUWXYZ"; // ="01" for binary counter
char lo[]="123456789";			 // ="01"
int bred(char *inout,int size) {
	char *s,*p;
	char *h,*l;
	int len;
	int shift;
	if (inout==NULL || size<2 || (len=strlen(inout))<2 || (len+1)%3)
		return -1; // arguments
	shift=1;	
	for(s=inout+len-1;shift && s!=inout;s--) {
		if ( (l=strchr(lo,*s--)) == NULL) return -1; // !allowed hi
		if ( (h=strchr(hi,*s)) == NULL) return -1;	// !allowed lo
		shift=0;
		l++;
		if (0==*l) {
			l=lo;
			h++;
			if (0==*h) {
				h=hi;
				shift=1;
			};
		}
		s[0]=*h;
		s[1]=*l;
		if (s--==inout) break;
		if (*s!='-')return -1; //delimiter
	}
	if (shift) {
		if (len+4>size) 
			return -1;	// buffer size
		inout[len++]='-';
		inout[len++]=hi[0];
		inout[len++]=lo[0];
		inout[len++]=0;
	}
	return 0;
}
int main() {
	char in[128];
	char out[128];
	int len;
	while(!feof(stdin)) {
		printf(">");fflush(stdout);
		if (fgets(in,128,stdin)==NULL) return -1;
		if (in[0]!='\n') {
			len=strcspn(in,"\n");
			strncpy(out,in,len);
			out[len]=0;
		}
		if (bred(out,128)!=0) {
			printf("Bred failed\n");
		} else {
			printf("Bred completed:%s\n",out);
		}
	}
	return 0;
}

MKuznetsov ★★★★★
()
Ответ на: [ansi-c] как-то так от arsi

валидация написана нормально

inctab разумна

а вот в инкременте кусок, непонятный даже после некоторого разлядывания

кроме того, он у тебя случаем

1. не пытается ли писать в память только для чтения?

2. не пытается ли выйти за границы памяти после инкремента? (если память аллоцируется магическими блоками в 32 байта, то тогда *надо* написать аллокатор)

короче, обычные неудобства си

кроме того, валидацию нас не просили делать, поэтому ее можно отложить на потом (хотя, с другой стороны, слово «класс» может ее подразумевать)

#include <iostream>
#include <string>

/// рассчитана на любой символ
/// невалидные (и '-') оставляет как есть
/// на валидном символе получает правильный результат
inline char increment(char c)
{
    static const signed char inctab[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ///(0) 1  2  3  4  5  6  7  8  9
        0, 1, 1, 1, 1, 1, 1, 1, 1,-8, 0, 0, 0, 0, 0, 0,
    ///    A  B  C (D) E (F)(G) H  I (J) K  L (M) N  O 
        0, 1, 1, 2, 0, 3, 0, 0, 1, 2, 0, 1, 2, 0, 1, 1, 
    /// P (Q) R  S  T  U (V) W  X  Y  Z
        2, 0, 1, 1, 1, 2, 0, 1, 1, 1,-25
    };
    return c+inctab[c];
}

/// рассчитана на любую строку, в т.ч. невалидную
/// на валидной строке получает правильный результат
inline std::string increment(const std::string& s)
{
    std::string result=s;
    bool overflow=true;
    for( int i=s.size()-1; i>=0; --i ) {
      if( overflow ) {
        result[i] = increment(result[i]);
        overflow = (result[i]=='A') || (result[i]=='1') || (result[i]=='-');
      }
    }
    if( overflow )
      result = "A1-" + result;
    return result;
}    
int main(int argc, char** argv)
{
  if( argc<2 )
      return 1;
  std::string s=argv[1];
  std::cout << (s=increment(s)) << '\n';
  std::cout << (s=increment(s)) << '\n';
  std::cout << (s=increment(s)) << '\n';
  return 0;
}
www_linux_org_ru ★★★★★
()
Ответ на: комментарий от MKuznetsov

посмотри на свою сложнятину...

у тебя совесть есть после этого критиковать плюсы?

но по крайней мере наборы символов не вшиваются жестко в код, а задаются из вне функции.

это хорошо, только сделано у тебя не до конца

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

насчет «не до конца» — был не прав, скроллил твой код и не заметил, как попал на код ТС с его while(*p == 'D'

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

у тебя совесть есть после этого критиковать плюсы?

есть..ни одно предложенное выше «решение на плюсах» не позволяет пользователю самому в простом виде задавать _независимые_ наборы символов для этих двухразрядных счётчиков. ВСЕ кто предложил С++ вшили и размазали свойства алфавита по всему коду.

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Эстафета, так эстафета. Вы меня вынуждаете... :)


#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define MAX_NODES 10

int incr_r(char* value, size_t pos) {

	int result = pos > 0 ? incr_r(value, pos - 1) : 1;

	if (result) {
		char *v = value + pos;
		if ((*v >= '1' && *v <= '8') || (*v >= 'A' && *v <= ('Z' - 1)))
			(*v)++, result = 0;
		else if (*v == '9')
			*v = '1';
		else if (*v == 'Z')
			*v = 'A';
		else if (*v == '-')
			/*nothing*/;
		else
			fprintf(stderr, "Invalid character '%c'\n", *v), exit(-1);
	}

	return result;
}

int incr(char* value, size_t size) {
	size_t l = strlen(value);
	int i, v;
	for (i = 0; i < l / 2; i++)
		v = value[i], value[i] = value[l - i - 1], value[l - i - 1] = v;

	int result = incr_r(value, l - 1);

	for (i = 0; i < l / 2; i++)
		v = value[i], value[i] = value[l - i - 1], value[l - i - 1] = v;

	if (result && l + 3 < size)
		memmove(value + 3, value, l),
		memcpy(value, "A1-", 3),
		result = 0;

	return result;
}

#define BUFF_LEN (MAX_NODES * 3)

int main(int argc, char** argv) {
	int i;
	char* value = malloc(sizeof(char) * BUFF_LEN);
	for (i = 1; i < argc; i++) {
		strcpy(value, argv[i]);
		int result = incr(value, BUFF_LEN);
		printf("%s%s\n", value, result ? " overflow" : "");
	}
	return 0;
}

Отличия от исходного задания:

1. Не стал делать следующее, т.к. было лень:

В идентификаторах никогда не должны присутствовать буквы «D», «F», «G», «J», «M», «Q», «V»,

2. Цифровые и буквенные знаки и знак дефиса могут располагаться как угодно.

geekless ★★
()
Ответ на: комментарий от MKuznetsov

> есть..ни одно предложенное выше «решение на плюсах» не позволяет пользователю самому в простом виде задавать _независимые_ наборы символов для этих двухразрядных счётчиков. ВСЕ кто предложил С++ вшили и размазали свойства алфавита по всему коду.

Хых. Я тоже хотел написать на Сях (без плюсов) вариант, в котором алфавит задаётся отдельно. Но потом увидел код arsi, и стало влом.

geekless ★★
()

Вернее так должна incr() выглядеть:

int incr(char* value, size_t size) {
	size_t l = strlen(value);
	int i, v;
	for (i = 0; i < l / 2; i++)
		v = value[i], value[i] = value[l - i - 1], value[l - i - 1] = v;

	int result = incr_r(value, l - 1);

	if (result && l + 3 < size)
		strcat(value, "-1A"),
		result = 0;

	for (i = 0; i < l / 2; i++)
		v = value[i], value[i] = value[l - i - 1], value[l - i - 1] = v;

	return result;
}
geekless ★★
()
Ответ на: комментарий от www_linux_org_ru

> 1. не пытается ли писать в память только для чтения?

где? о_О

> 2. не пытается ли выйти за границы памяти после инкремента?

—//—

а вот с магическими числами согласен; мало того, что непонятно, так еще и 2+2 сложить не смог: у меня максимум 11 групп получается…

@@ -2,6 +2,8 @@
 #include <string.h>
 #include <ctype.h>
 
+#define MAX_GROUPS    10
+#define MAX_LENGTH    ((MAX_GROUPS)*2+(MAX_GROUPS)-1)
 
 static int is_valid(const char *p) {
     if (p == NULL || strlen(p) != strspn(p, "ABCEHIKLNOPRSTUWXYZ123456789-"))
@@ -33,7 +35,7 @@
         *t-- = '1';
         if ((*t += inctab[*t - 'A']) != 'A')
             break;
-        if (t == p && strlen(p) < 32)
+        if (t == p && strlen(p) < MAX_LENGTH)
             strcat(p, "-A1");
     }
 
@@ -41,7 +43,7 @@
 }
 
 int main() {
-    char str[33] = "Z9-Z8";
+    char str[MAX_LENGTH + 1] = "Z9-Z8";
 
     puts(str);
     inc(str);

зы: только у меня подсветка для

 не работает?

arsi ★★★★★
()
Ответ на: эстафета от do0dlez

> эстафета

#!/usr/bin/perl

use strict;
use warnings 'all';
use 5.014;

my $chars = 'ABCEHIKLNOPRSTUWXYZ';

sub inc($) {
    local $_ = shift;
    return undef if !/^[$chars][1-9](-[$chars][1-9]){0,9}$/
                 || /^Z9(-Z9){9}$/;
    return $_    if s!^Z9(-Z9){0,8}$!A1-$&! && y/Z9/A1/
                 || s!(.)([1-8])((?:-Z9)*)$!$1.($2+1).($3=~y/Z9/A1/r)!e
                 || s!([^Z])9((?:-Z9)*)$!($1=~y/CEILPUA-Y/EHKNRWB-Z/r).'1'.($2=~y/Z9/A1/r)!e;
}

my $t = 'A1';
say $t = inc $t for 1..100000;
arsi ★★★★★
()
Ответ на: комментарий от arsi

ах да, там же ООП требовался…

#!/usr/bin/perl

package Foo;
use strict;
use warnings 'all';
use 5.014;

my $chars = 'ABCEHIKLNOPRSTUWXYZ';

use overload '++' => sub {
    my $self = shift;
    my $_ = $self->{str};
    return undef if /^Z9(-Z9){9}$/;
    s!^Z9(-Z9){0,8}$!A1-$&! && y/Z9/A1/
    || s!(.)([1-8])((?:-Z9)*)$!$1.($2+1).($3=~y/Z9/A1/r)!e
    || s!([^Z])9((?:-Z9)*)$!($1=~y/CEILPUA-Y/EHKNRWB-Z/r).'1'.($2=~y/Z9/A1/r)!e;
    $self->{str} = $_;
    return $self;
};

use overload '""' => sub { shift->{str} };

sub new {
    my ($class, $_) = @_;
    return undef unless defined && /^[$chars][1-9](-[$chars][1-9]){0,9}$/;
    return bless {str=>$_}, $class;
}


package main;
use strict;
use warnings 'all';

my $t = Foo->new('A1') or die;
say ++$t for 1..100000;
arsi ★★★★★
()
Ответ на: комментарий от MKuznetsov

ни одно предложенное выше «решение на плюсах» не позволяет пользователю самому в простом виде задавать _независимые_ наборы символов для этих двухразрядных счётчиков. ВСЕ кто предложил С++ вшили и размазали свойства алфавита по всему коду.

cомневаюсь что это нужно сразу — да и размазанность не по всему коду, а по двум функциям (или как просили ТС по классу) это приемлемо; впрочем, антиразмазать можно относительно просто:

//  usage:  const char* chars[] = { "ABCEHIKLNOPRSTUWXYZ", "123456789", "-", "" };
void init_them(const char* chars[])
{
    for( int i=0; chars[i+1][0]; ++i) {
        first_char.push_back(chars[i][0]);
        if( chars[i+1][0] )
            continue;
        for(int j=0; chars[i][j]; ++j) {
            inctab[ (unsigned)chars[i][j] ] = ( chars[i][j+1] ? chars[i][j+1] : chars[i][0] ) - chars[i][j];
        }
    }
}

но напрямую ручками составленная inctab может быть более понятна, чем такой код

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от arsi

еще, кстати, в inctab видимо лучше сразу писать тот символ, который там должен быть после инкремента, а не разницу — х.з. че там случится от складывания неизвестно-как-знакового чара со знаковым (хотя от сложения подлянка вряд ли будет, но от шифтов направо — очень даже м.б.)

а насчет перла я щас подумаю тоже...

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> х.з. че там случится от складывания неизвестно-как-знакового чара

> static const signed char inctab[26] = …

да уж, действительно, неизвестно-как-знакового…

> в inctab видимо лучше сразу писать тот символ, который там должен быть после инкремента

согласен)

arsi ★★★★★
()
Ответ на: комментарий от arsi

Re: в inctab видимо лучше сразу писать тот символ, который там должен быть после инкремента

@@ -22,10 +22,9 @@
 }
 
 static int inc(char *p) {
-    static const signed char inctab[26] = {
-    /*  A B C d E f g H I j K L m N O P q R S T U v W X Y Z */
-        1,1,2,0,3,0,0,1,2,0,1,2,0,1,1,2,0,1,1,1,2,0,1,1,1,-25
-    };
+    static const char next[26] =
+    /*   ABCdEfgHIjKLmNOPqRSTUvWXYZ */
+        "BCE-H--IK-LN-OPR-STUW-XYZA";
     char *t;
 
     if (!is_valid(p))
@@ -33,7 +32,7 @@
 
     for (t = strchr(p, '\0') - 1; t > p && ++*t > '9'; t -= 2) {
         *t-- = '1';
-        if ((*t += inctab[*t - 'A']) != 'A')
+        if ((*t = next[*t - 'A']) != 'A')
             break;
         if (t == p && strlen(p) < MAX_LENGTH)
             strcat(p, "-A1");

зы: инкрементальный патч, зависит от предыдущего.

arsi ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> я про char *t

для *t (т.е. *t - 'A') — фиолетово, какая знаковость у чара, т.к. переполнения в данном случае даже теоретически быть не может, ни в какую сторону. единственное исключение: используется не ascii-compatible charset, но в таком случае весь мой код можно смело выбрасывать на свалку (ну, не весь, но функцию inc() — точно).

arsi ★★★★★
()
Ответ на: комментарий от arsi

я вот тут однострочник на перле накропал — читает строчки с числами и печатает инкрементированные

perl -Wpe 'chop;$_.="#";(s/-#/#-/,s/9#/#1/,s/Z#/#A/,s/^#/A1-/,s/([A-Z0-9])#/$r=chr(1+ord$1),($r=~m<[0DFGJMQV]>?"$r#":$r)/ge)while/#/;$_.="\n"'

багрепорты и патчи приветствуются

и да, в отличие от имевших место быть программ, здесь исключения заданы списком 0DFGJMQV

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

он, понятное дело, правильно инкрементирует только числа правильного формата, но зависание на любой строке будет багом (т.е. я не вижу такого)

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

упс... вот исправленный вариант

perl -Wpe 'chop;$_.="#";(s/-#/#-/,s/9#/#1/,s/Z#/#A/,s/^#/A1-/,s/([^-])#/$r=chr(1+ord$1),($r=~m<[0DFGJMQV]>?"$r#":$r)/ge)while/#/;$_.="\n"'
www_linux_org_ru ★★★★★
()
Ответ на: комментарий от arsi

>> 1. не пытается ли писать в память только для чтения?

где?

char str[33] = «Z9-Z8»;

эта строка «Z9-Z8» может лежать в RO памяти, а ты потом вызываешь ей inc(str) которая ее модифицирует

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> char str[33] = «Z9-Z8»;
> эта строка «Z9-Z8» может лежать в RO памяти, а ты потом вызываешь ей inc(str) которая ее модифицирует

омг о_О ты в стандарт языка когда-нибудь заглядывал? ;)

ISO/IEC 9899:1999 

6.7.8 Initialization

32  EXAMPLE 8   The declaration
         char s[] = "abc", t[3] = "abc";
    defines ‘‘plain’’ char array objects s and t whose elements are initialized with character string literals.
    This declaration is identical to
         char s[] = { 'a', 'b', 'c', '\0' },
              t[] = { 'a', 'b', 'c' };
    The contents of the arrays are modifiable. On the other hand, the declaration
         char *p = "abc";
    defines p with type ‘‘pointer to char’’ and initializes it to point to an object with type ‘‘array of char’’
    with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to
    modify the contents of the array, the behavior is undefined.

предложение «The contents of the arrays are modifiable.» надеюсь переводить нет необходимости ;)

arsi ★★★★★
()
Ответ на: комментарий от arsi

уж простите, лиспов с хацкелями не разумею

#!/usr/bin/env node

var hi = 'ABCEHIKLNOPRSTUWXYZ',
    lo = '123456789',
    cmax = 10;

function Crap(arg) {
    this.valid = false;
    var re = new RegExp('^['+hi+']['+lo+']'+'(-['+hi+']['+lo+']){0,'+cmax+'}$');
    if (!re.test(arg)) return this;
    this.syms = arg.split('-');
    for (var i in this.syms) {
        this.syms[i] = [hi.indexOf(this.syms[i][0]), lo.indexOf(this.syms[i][1])];
    }
    this.valid = true;
    return this;
}
Crap.prototype = {
    constuctor: Crap,
    inc: function () {
        if (!this.valid) return false;
        var ovf = true;
        for (var i = this.syms.length - 1; ovf && i >= 0; i--) {
            ovf = false;
            ++this.syms[i][1];
            if(this.syms[i][1] %= lo.length) break;
            ++this.syms[i][0];
            if(this.syms[i][0] %= hi.length) break;
            ovf = true;
        }
        if (ovf) this.syms.unshift([0, 0]);
        if (this.syms.length > cmax) {
            this.valid = false;
            this.syms = [];
        }
        return this;
    },
    toString: function () {
        var tmp = '';
        if (!this.valid) return '';
        for (var i = 0; i < this.syms.length; i++) {
            tmp += String(hi[this.syms[i][0]]) + lo[this.syms[i][1]];
            if (i + 1 < this.syms.length) tmp += '-';
        }
        return tmp;
    }
}

var lol = new Crap('A1');
for (var i = 0; i < 200; i++) {
    console.log(''+lol);
    lol.inc();
    lol = new Crap(lol.toString());
}

lol = new Crap('Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z5');
for (var i = 0; i < 10; i++) {
    console.log(''+lol);
    lol.inc();
    lol = new Crap(lol.toString());
}
anonymous
()
Ответ на: комментарий от arsi

> омг о_О ты в стандарт языка когда-нибудь заглядывал? ;)

в стандарт именно си года 2-3 назад

я уж и забыл, что си предоставляет какой-то сервис по копированию литерала в модифицируемый массив (неважно, что копирование происходит во время компиляции); обычно модификация может потребовать изменения размера, так что я для этого пользуюсь чем-нить из плюсовых контейнеров

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

s/происходит/может происходить/

и да, внимательный взгляд на [33] мог бы прояснить ситуацию

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> неважно, что копирование происходит во время компиляции

копирование происходит после входа в функцию, т.е. в рантайме.

arsi ★★★★★
()

Немного экзотики:

$ cat ./test.fs
\ -------------------------------------------------------------------------  
: arrayOFchar { charFIRST charLAST -- }
  create 
    0 charFIRST begin
                  swap 1+ swap dup c, 1+ 
                  dup case
                        [char] D of 1+ endof
                        [char] F of 1+ endof
                        [char] G of 1+ endof
                        [char] J of 1+ endof
                        [char] M of 1+ endof
                        [char] Q of 1+ endof
                        [char] V of 1+ endof
                      endcase
                  dup charLAST >
                  until drop
  does> ;
: arrayOFnumb { charFIRST charLAST -- }
  create 0 charFIRST begin 
                       swap 1+ swap dup c, 1+ 
                     dup charLAST > 
                     until drop
  does> ;
\ -------------------------------------------------------------------------  
char A char Z arrayOFchar arrCHR constant dimCHR
char 1 char 9 arrayOFnumb arrNMB constant dimNMB
              dimCHR dimNMB *    constant dimSCT
\ -------------------------------------------------------------------------  
: printSECTION { nmb -- } assert( nmb dimSCT < )
  nmb dimNMB /mod arrCHR + c@ emit arrNMB + c@ emit ;
: printNUMBER recursive { nmb -- } assert( nmb 0 >= )
  nmb dimSCT /mod 
  dup if 1- printNUMBER ." -" else drop endif
  printSECTION ;
: indexCHAR { chr addr cnt -- indx }
  0 begin dup addr + c@ chr = invert
    while 1+ assert( dup cnt <= )
    repeat ;
: recognizeSECTION { addr -- number } 
  addr    c@ arrCHR dimCHR indexCHAR dimNMB * 
  addr 1+ c@ arrNMB dimNMB indexCHAR + ;
: recognizeNUMBER { caddr u -- number }
  0 0 begin dup u <
      while
        >r caddr r@ + recognizeSECTION + r> 
	    dup caddr + 1+ 1+ c@ [char] - = 
	    if swap 1+ dimSCT * swap endif
  3 + repeat drop ;
: increment: parse-word recognizeNUMBER 1+ printNUMBER ;
\ -------------------------------------------------------------------------  
: test: 13 parse 2dup type ."  = " evaluate CR ;
CR 
test:     0 printNUMBER
test:     1 printNUMBER
test:     9 printNUMBER
test:   179 printNUMBER
test:   180 printNUMBER
test:   359 printNUMBER
test:   360 printNUMBER
test: 32579 printNUMBER
test: 32580 printNUMBER
test: 32581 printNUMBER
CR
test:       s" A1" recognizeNUMBER .
test:    s" A1-A1" recognizeNUMBER .
test:    s" A1-Z9" recognizeNUMBER .
test: s" A1-A1-A1" recognizeNUMBER .
test: s" A1-A1-Z9" recognizeNUMBER .
CR
test: increment:          A1
test: increment:          A2
test: increment:          Z9
test: increment:       A1-A1
test: increment:       A1-Z9
test: increment:    A1-A1-A1
test: increment:    A1-Z9-Z9
test: increment:    A1-A2-Z9
test: increment:    A3-A8-Z9
test: increment:    A5-Z9-Z9
test: increment: C3-A5-Z9-Z9
test: increment: C3-K5-Z9-Z9
test: increment: C3-Z9-Z9-Z9
CR
test:       s" A1" recognizeNUMBER     s" A1" recognizeNUMBER + printNUMBER
test:       s" A1" recognizeNUMBER                          5 + printNUMBER
test:    s" A1-A1" recognizeNUMBER     s" Z1" recognizeNUMBER + printNUMBER
test:    s" A1-Z9" recognizeNUMBER                        101 * printNUMBER
test: s" A1-A1-A1" recognizeNUMBER  s" A1-A1" recognizeNUMBER + printNUMBER
test: s" A1-A1-A1" recognizeNUMBER  s" A1-A1" recognizeNUMBER * printNUMBER
test: s" A1-Z8-A2" recognizeNUMBER  s" K1-Z9" recognizeNUMBER - printNUMBER
\ -------------------------------------------------------------------------  
BYE
Результат:
$ gforth ./test.fs 

    0 printNUMBER = A1
    1 printNUMBER = A2
    9 printNUMBER = B1
  179 printNUMBER = Z9
  180 printNUMBER = A1-A1
  359 printNUMBER = A1-Z9
  360 printNUMBER = A2-A1
32579 printNUMBER = Z9-Z9
32580 printNUMBER = A1-A1-A1
32581 printNUMBER = A1-A1-A2

      s" A1" recognizeNUMBER . = 0 
   s" A1-A1" recognizeNUMBER . = 180 
   s" A1-Z9" recognizeNUMBER . = 359 
s" A1-A1-A1" recognizeNUMBER . = 32580 
s" A1-A1-Z9" recognizeNUMBER . = 32759 

increment:          A1 = A2
increment:          A2 = A3
increment:          Z9 = A1-A1
increment:       A1-A1 = A1-A2
increment:       A1-Z9 = A2-A1
increment:    A1-A1-A1 = A1-A1-A2
increment:    A1-Z9-Z9 = A2-A1-A1
increment:    A1-A2-Z9 = A1-A3-A1
increment:    A3-A8-Z9 = A3-A9-A1
increment:    A5-Z9-Z9 = A6-A1-A1
increment: C3-A5-Z9-Z9 = C3-A6-A1-A1
increment: C3-K5-Z9-Z9 = C3-K6-A1-A1
increment: C3-Z9-Z9-Z9 = C4-A1-A1-A1

      s" A1" recognizeNUMBER     s" A1" recognizeNUMBER + printNUMBER = A1
      s" A1" recognizeNUMBER                          5 + printNUMBER = A6
   s" A1-A1" recognizeNUMBER     s" Z1" recognizeNUMBER + printNUMBER = A1-Z1
   s" A1-Z9" recognizeNUMBER                        101 * printNUMBER = A1-C3-L8
s" A1-A1-A1" recognizeNUMBER  s" A1-A1" recognizeNUMBER + printNUMBER = A1-A2-A1
s" A1-A1-A1" recognizeNUMBER  s" A1-A1" recognizeNUMBER * printNUMBER = Z9-Z9-A1
s" A1-Z8-A2" recognizeNUMBER  s" K1-Z9" recognizeNUMBER - printNUMBER = A1-R6-A3
В общем, тут есть немного арифметики этих ваших странных чисел, в предела 4-байтовых положительных целых.

P.S. За код сильно не пинайте, что в голову пришло на ночь глядя, то и рисовал. Не ООП.

Neksys ★★★
()
Ответ на: комментарий от www_linux_org_ru
$ echo A3-K5-Z9 | perl -Wpe 's/(\s*)$/#$1/;(s/-#/#-/,s/9#/#1/,s/Z#/#A/,s/^#/A1-/,s/([^-])#/$r=chr(1+ord$1),($r=~m<[0DFGJMQV]>?"$r#":$r)/ge)while/#/'
A3-K6-A1

Это ппц, оно ещё и работает. 0_o

Снимаю шляпу. Осталось разобраться, как оно это делает. :)

Neksys ★★★
()

А вот и lithp:

#lang racket
(require syntax/parse/define)

(define-simple-macro (counter first arg ... last)
  #:with (a ...) #`(first arg ...)
  #:with (b ...) #`(arg ... last)
  (lambda (x) (case x
                [(a) (values b #f)] ...
                [(last) (values first #t)]
                [else (error "counter error in:" x)])))

(define-syntax chain-counter
  (syntax-rules ()
    [(chain-counter)
     (λ (lst) (values `() #t))]
    [(chain-counter counter ... last) 
     (lambda (lst) 
       (let-values ([(value overflow) ((chain-counter counter ...) (cdr lst))])
         (if overflow
             (let-values ([(cur-value overflow) (first (car lst))])
               (values (cons cur-value value) overflow))
             (values (cons (car lst) value) #f))))]))

(define (rec-counter counter)
  (lambda (lst) 
    (if (null? lst) 
        (values `() #t)
        (let-values ([(value overflow) ((rec-counter counter) (cdr lst))])
          (if overflow 
              (let-values ([(cur-value overflow) (counter (car lst))])
                (values (cons cur-value value) overflow))
              (values (cons (car lst) value) #f))))))

;----------------------------------------------------------------------------------------------------------

(define mycounter 
  (rec-counter (chain-counter (counter #\A #\B #\C #\E #\F #\H #\I #\K #\L #\N #\O #\P #\R #\S #\T #\U #\W #\X #\Y #\Z)
                              (counter #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))))

(define (next str)
  (string-join (map list->string 
                    (let-values ([(value overflow) (mycounter (map string->list (regexp-split #rx"-" str)))])
                      (if overflow
                          (cons (list #\A #\1) value)
                          value)))
               "-"))

anonymous
()
Ответ на: комментарий от www_linux_org_ru

На трезвую голову заметил косяк:

$ echo K3-Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z9-D1 | perl -Wpe 's/(\s*)$/#$1/;(s/-#/#-/,s/9#/#1/,s/Z#/#A/,s/^#/A1-/,s/([^-])#/$r=chr(1+ord$1),($r=~m<[0DFGJMQV]>?"$r#":$r)/ge)while/#/'
K3-Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z9-Z9-D2
$ echo K3-Z9-Z9-Z9-Z9-Z9-Z9-Z9-D0 | perl -Wpe 'chop;$_.="#";(s/-#/#-/,s/9#/#1/,s/Z#/#A/,s/^#/A1-/,s/([^-])#/$r=chr(1+ord$1),($r=~m<[0DFGJMQV]>?"$r#":$r)/ge)while/#/;$_.="\n"'
K3-Z9-Z9-Z9-Z9-Z9-Z9-Z9-D1

«D» и «0» должны быть запрещены. Будет мне пищей для размышлений на выходные.

P.S. Но всё равно прикольно. :)

Neksys ★★★
()
Ответ на: комментарий от Neksys

Осталось разобраться, как оно это делает.

очень просто — вот так:

$$ echo E9-Z9-Z9 | perl -Wpe 's/(\s*)$/#$1/;print;(s/-#/#-/,print,s/9#/#1/,print,s/Z#/#A/,print,s/^#/A1-/,print,s/([^-])#/$r=chr(1+ord$1),($r=~m<[0DFGJMQV]>?"$r#":$r)/ge,print)while/#/'
E9-Z9-Z9#
E9-Z9-Z9#
E9-Z9-Z#1
E9-Z9-#A1
E9-Z9-#A1
E9-Z9-#A1
E9-Z9#-A1
E9-Z#1-A1
E9-#A1-A1
E9-#A1-A1
E9-#A1-A1
E9#-A1-A1
E#1-A1-A1
E#1-A1-A1
E#1-A1-A1
F#1-A1-A1
F#1-A1-A1
F#1-A1-A1
F#1-A1-A1
F#1-A1-A1
G#1-A1-A1
G#1-A1-A1
G#1-A1-A1
G#1-A1-A1
G#1-A1-A1
H1-A1-A1
H1-A1-A1
$ 
www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Neksys

> «D» и «0» должны быть запрещены.

она не валидирует вход, но на валидном входе не выдает [0DFGJMQV]

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Neksys

Фикс во имя луны (вдруг кто и взаправду будет разбирать код):

\ -------------------------------------------------------------------------  
s" ABCEHIKLNOPRSTUWXYZ" constant dimCHR constant arrCHR
s" 123456789"           constant dimNMB constant arrNMB
dimCHR dimNMB *         constant dimSCT
\ -------------------------------------------------------------------------  
: printSECTION  { nmb -- } assert( nmb dimSCT < )
  nmb dimNMB /mod  ( rem div -- )
  arrCHR + c@ emit 
  arrNMB + c@ emit ;
: printNUMBER recursive  { nmb -- } assert( nmb 0 >= )
  nmb dimSCT /mod  ( rem div -- )
  dup if 1- printNUMBER ." -" else drop endif
  printSECTION ;
: indexCHAR  { chr caddr u -- indx }
  0 
    begin 
      dup caddr + c@ chr = invert
    while 
      1+ assert( dup u <= )
    repeat ;
: recognizeSECTION  { caddr -- number } 
  caddr    c@ arrCHR dimCHR indexCHAR  dimNMB * 
  caddr 1+ c@ arrNMB dimNMB indexCHAR  + ; 
: recognizeNUMBER  { caddr u -- number }
  0 0 
      begin 
        dup u <
      while 
        >r caddr r@ + recognizeSECTION + r> 
        dup caddr + 1+ 1+ c@ [char] - = 
        if >r 1+ dimSCT * r> endif
        3 + 
      repeat
    drop ;
: increment: 
  parse-word 
  recognizeNUMBER 1+ printNUMBER ;
\ -------------------------------------------------------------------------  
: test: 13 parse 2dup type ."  = " evaluate CR ;
\ : test: 13 parse 2drop ;
CR 
В старой версии лишняя буковка «G» в алфавит затесалась. Sad but true.
Тест-пак можно использовать старый.
Немного отформатировал код и упростил объявления, ибо сии массивы по сути строки они есть. :)

Neksys ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.