金沙国际官网_金沙国际平台登录

因为这个金沙国际官网_金沙国际平台登录网站与很多的大型澳门赌场都有合作,金沙国际官网_金沙国际平台登录尽职尽责,高效执行,保持好奇心,不断学习,追求卓越,点击进入金沙国际官网_金沙国际平台登录马上体验吧,所以现在也正式地开始了营业。

您的位置:金沙国际官网 > 编程 > 理解希尔排序,浅谈自学Python之路

理解希尔排序,浅谈自学Python之路

发布时间:2019-11-04 04:08编辑:编程浏览(190)

    本文版权归博客园和作者吴双本人共同所有。欢迎转载,转载和爬虫请注明原文地址 

    我的博客链接:理解希尔排序

    2018-02-19  17:15:14

    昨天晚上,有个朋友说学了好久,依然没搞懂多态,让我简单讲解一下。我觉得多态在面向多想的三大特性当中,算是最简单的,最难的是看似容易的封装。在编写面向对象代码时,如何让代码可读性更强,除了变量和方法命名标准外,要做的到一个方法只做一件事情,这样的思想是《代码整洁之道》一书中主要推崇的思想,其实有经验的各位都希望自己看到的代码是简短,可维护,可读性强的,相信大家也都“有幸”遇到过几百上千行的代码,更过分的是有个朋友曾经维护一个上万行的Action,夸张的说,调试并走通逻辑,一次要三天,有的人说这是业务逻辑不断增加所导致,但我认为,在这种情况下,更应该尽量做到一个方法做一件事情。我也不多吐槽了,关于代码整洁,我在大三的时候,就"吐槽"过。

    最近回顾了一下 《The C Programming Language》,其中提到了一个用来演示 for 循环的小例子,如下:

    Python语言相对于其他语言较为简洁,也相对好入门比如后面不加分号,基本见不着大括号等优点

    封装也不是今天的主题,今天我们要说的是多态,在朋友问我的时候,我给他举了下面这个简短的例子。

    /** shell sort */
    void shellsort(int[], int);
    
    int main()
    {
        int a[] = {1,23,34,24,32,25,31,3,36,40};
        int b[] = {32,31,30,29,28,27,26,25,24,23};
        shellsort(b, 10);
    }
    
    void shellsort(int v[], int n)
    {
        int gap, i, j, temp;
    
        for(gap = n/2; gap > 0; gap /= 2) {
            for(i = gap; i<n; i++) {
                for(j=i-gap; j>=0 && v[j] > v[j+gap]; j-=gap) {
                    temp = v[j];
                    v[j] = v[j+gap];
                    v[j+gap] = temp;
                }
            }
        }
    }
    

     

    总体概括这个例子来讲就是在基本的三层架构当中,DAL层建两个类AdminDal,UserDal。两个类中,都有增加对象和删除对象地方法,那这个时候,我们应该给两个类抽象出一个父类BaseDal<T>,父类中是他们的公共方法,并且父类需要一个泛型T,这样父类的方法,才能明白你所要添加或者删除的object到底是什么类型的。请看如下代码。虽然两个类的公共方法在父类当中,但是他们自身特有的方法,还是要写在自己的Dal层当中。

    这是一个希尔排序的例子,以每次 n/2 为步长,比较步长两边的元素的大小,步长是从大到小的,也就是说,一开始直接比较相距较远的两个元素,如果是逆序,则直接交换,比基于相邻比较的排序(冒泡排序,交换排序)跨越了更多的中间位置;然后最终步长为1能够保证所有元素都能正确被排序。步长为1时,退化为交换排序,但是其实这时序列是已经经过排序的,所以要比一开始就用交换排序要好。

    • 第一个程序,也是学每门语言都需要掌握的第一个代码
    1   public class UserDal: BaseDal<UserEntity>
    2   {
    3         
    4   }
    
    1   public class AdminDal: BaseDal<AdminEntity>
    2     {
    3         public void Manage()
    4         {
    5             Console.WriteLine("管理员管理网站");
    6         }
    7     }
    
     1 public class BaseDal<T>
     2     {
     3         public void AddObj(T obj)
     4         {
     5             Console.WriteLine("添加对象成功,对象属于"+obj.GetType().ToString());
     6         }
     7 
     8         public void DeleteObj(T obj)
     9         {
    10             Console.WriteLine("删除对象成功,对象属于"+obj.GetType().ToString());
    11         }
    12 
    13     }
    

    希尔排序是第一批跨越 O(n2) 复杂度的排序算法之一。它是一种不稳定的排序算法,其性能与步长的取值有很大关系,Wikipedia上有关于各种步长选择下的性能比较:Shellsort#Gap_sequences。

     print("Hello World") 

     下面给出逻辑层代码,如果说普通的开发过程当中,你的代码也许是这样的。

    为了帮助理解希尔排序的排序过程,可以看这个演示视频(需FQ): Shell Sort Algorithm Example

    • print 语句用法

      1 print 'a', 'b' #print可以接受多个参数,参数的输出之间以空格相隔。 2 print 'a', #如果逗号之后没有参数,则不会换行。 3 print 'b'

    • 接下来是输入用户名密码

      1 import getpass 2 3 _username = 'lym' 4 _password = 'abc123' 5 username = input("username:") 6 #password = getpass.getpass("password:") 7 password = input("password:") 8 if _username == username and _password == password: 9 print("Welcome user {name} login...".format(name=username)) 10 else: 11 print("Invalid username or password!")

    • 进行格式化拼接

      1 name = input("name:") 2 #age = input("age:") 3 age = int(input("age:")) 4 print(type(age), type( str(age) )) 5 job = input("job:") 6 salary = input("salary:") 7 8 #用这种比较好 9 info = ''' 10 --------------info of %s 11 Name:%s 12 Age:%d 13 Job:%s 14 Salary:%s 15 '''%(name,name,age,job,salary) 16 17 18 info2 = ''' 19 --------------info of {_name} 20 Name:{_name} 21 Age:{_age} 22 Job:{_job} 23 Salary:{_salary} 24 '''.format( 25 _name = name, 26 _age = age, 27 _job = job, 28 _salary = salary 29 ) 30 31 32 info3 = ''' 33 --------------info of {0} 34 Name:{0} 35 Age:{1} 36 Job:{2} 37 Salary:{3} 38 '''.format(name,age,job,salary) 39 40 print(info3)

     1  public class UserBll 
     2     {
     3         UserDal dal = new UserDal();
     4 
     5         public void Add(UserEntity obj)
     6         {
     7             dal.AddObj(obj);
     8         }
     9 
    10         public void Delete(UserEntity obj)
    11         {
    12             dal.DeleteObj(obj);
    13         }
    14      }
    
        public class AdminBll 
        {
            AdminDal dal = new AdminDal();
    
            public void Add(AdminEntity admin)
            {
                dal.AddObj(admin);
            }
            public void Delete(AdminEntity admin)
            {
                dal.DeleteObj(admin);
            }
    
            public void Manage()
            {
                dal.Manage();
            }
         }
    

    由以上可以看出,想输入一个英文或者数字可以这样写

    也就是在各自的逻辑层当中,调用dal层。这个时候你又看到依然有这么多重复的代码,是不是应该再次封装成一个BaseBll<T>呢。答案是肯定的,但是问题又来了,在封装父类的过程中,你会发现,这个dal的对象怎么封装呢?这就是用到多态的关键点。下面看一下BaseBll.cs的代码。

     1 name = input("name:") 

     public abstract class BaseBll<T> where T:class, new()
        {
            public BaseDal<T> currentDal;
    
            public BaseBll()
            {
                SetCurrentDal();
            }
    
            public abstract void SetCurrentDal();
    
    
            public void BaseAdd(T obj)
            {
                currentDal.AddObj(obj);
            }
    
            public void BaseDelete(T obj)
            {
                currentDal.DeleteObj(obj);
            }
    
        }
    

    简单而快捷

    我给了一个抽象的基类,并且给出抽象的SetCurrentDal的抽象方法定义。该方法用于设置当前类的currentDal到底是adminDal还是userDal。我们在构造函数中调用SetCurrentDal这个抽象方法,为什么在构造函数中调用的原因是,当实例化子类对象时,一定是首先进入其父类的构造函数。当子类AdminBll和UserBll继承BaseBll<T>的时候,必须重写抽象方法,并且为BaseDal<T> currentDal对象设置实际的值。我先给出子类的代码

     

     1 public class AdminBll : BaseBll<AdminEntity>
     2     {
     3         AdminDal dal = new AdminDal();
     4         public AdminBll()
     5         {
     6 
     7         }
     8         public void Manage()
     9         {
    10             new AdminDal().Manage();
    11         }
    12 
    13         public override void SetCurrentDal()
    14         {
    15             currentDal = new AdminDal();
    16         }
    17     }
    
    1 public class UserBll : BaseBll<UserEntity>
    2     {
    3         public override void SetCurrentDal()
    4         {
    5             base.currentDal = new UserDal();
    6         }
    7     }
    
    • format方法

    当实例化子类的对象时,过程为:子类构造函数(不进入)—进入父类构造函数—父类构造内部调用子类重写的SetCurrentDal(当前多态的currentDal到底是谁的实例)—父类构造执行完毕(设置currentDal完成)—子类构造函数。这就是抽象方法实现的多态。

    有时我们并不想用其他信息来构造字符串。这儿format()方法就很有用。

    下面在UI层调用一下,看看结果:

    python从2.6开始支持format,新的更加容易读懂的字符串格式化方法,

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             AdminBll adminBll = new AdminBll();
     6             AdminEntity admin = new AdminEntity() {AdminName="吴双",AdminPwd="123" };
     7             adminBll.Manage();
     8             adminBll.BaseAdd(admin);
     9             Console.ReadKey();
    10         }
    11     }
    

    从原来的% 模式变成新的可读性更强的

    输出结果:

    1. 花括号声明{}、用于渲染前的参数引用声明, 花括号里可以用数字代表引用参数的序号, 或者 变量名直接引用。

    2. 从format参数引入的变量名 、

    3. 冒号:、

    4. 字符位数声明、

    5. 空白自动填补符 的声明

    6. 千分位的声明

    7. 变量类型的声明: 字符串s、数字d、浮点数f

    8. 对齐方向符号 < ^ >

    9. 属性访问符中括号 ☐

    10. 使用惊叹号!后接a 、r、 s,声明 是使用何种模式, acsii模式、引用__repr__ 或 __str__

    11. 增加类魔法函数__format__(self, format) , 可以根据format前的字符串格式来定制不同的显示, 如: ’{:xxxx}’  此时xxxx会作为参数传入__format__函数中。

    图片 1

    • 注释

     

    最简单的注释“#”

    在开发的过程中,也许你会有很多实体类,每个实体类都有各自的增删改查等其他共有方法,基于这样的情况,我们就需要手段来将其封装。为什么在逻辑层使用了多态,原因就是我们封装父类的时候,不确定当前的currentDal到底是adminDal还是userDal还是xxxDal。为了封装出基类,这个多态的对象就必不可少了。

    比如:

    当然在实际当中,如果你是写原生sql,这样封装的确不容易,各种拼接sql。但如果说你用ORM框架,EF,Dapper之类的,这个方法真的是必不可少的,你可能再加上接口层,加上工作单元,创建对象非new,使用抽象工厂,依赖注入等。无论怎样,这一层的多态一定能用到,只是创建对象稍作修改。

    #print("这是一个注释")
    

     

    多行注释: """ """

    本文由金沙国际官网发布于编程,转载请注明出处:理解希尔排序,浅谈自学Python之路

    关键词: